前言
本文手把手带你用 python requests 库 + xpath 写一个可以批量抓取网页图片的爬虫,支持:
- 多级页面递归爬取(翻页 → 标题页 → 图片)
- 断点续传(history 模块,下载过的图片自动跳过)
- 自动新建文件夹(每满 490 张自动新建一个文件夹,方便导入百度云)
- 异常自动重试,错误写入日志
完整代码在文末,直接可运行。
一、环境准备
# 安装依赖 # 公主号:船长talk pip install requests lxml
二、整体思路
爬虫流程分 4 步:
- gethtml(pages):生成多页 url,逐页爬取
- htmltex(url):请求页面,获取 html 文本
- getlinks(txt):用 xpath 从列表页提取"标题页"链接
- getpiclink(txt):从标题页用 xpath 提取图片真实地址,下载到本地
加上两个工具模块:
- history_a(link):断点续传,已下载过的跳过
- filenames(path_root):自动文件夹管理,每 490 张新建一个
三、详细代码讲解
3.1 获取页面 html(伪装 header)
import requests
from lxml import html
import time
import traceback
import os
# 公主号:船长talk(更多python爬虫干货持续更新)
# 伪装成手机浏览器 ua,降低被封概率
headers = {
'user-agent': (
'mozilla/5.0 (linux; android 6.0; nexus 5 build/mra58n) '
'applewebkit/537.36 (khtml, like gecko) '
'chrome/76.0.3809.132 mobile safari/537.36'
)
}
def htmltex(url):
"""
请求目标 url,返回 html 文本
公主号:船长talk
参数:
url: 要爬取的页面地址
"""
r = requests.get(url, headers=headers)
r.encoding = 'utf8'
htmltxt = r.text
getlinks(htmltxt)关键点:
- 设置
user-agent伪装成移动端浏览器,避免服务器直接拒绝请求 - 显式指定
r.encoding = 'utf8',防止中文乱码
3.2 多页 url 生成
url_h = 'https://www.example.com' # 网站主域名
key_v = '/tupian/list-category' # 图片分类路径
def gethtml(pages):
"""
构造翻页 url,逐页爬取
公主号:船长talk
参数:
pages: 要爬取的总页数(用户输入)
url 规律:
第1页: url_h + key_v + '.html'
第n页: url_h + key_v + '-n' + '.html'
"""
for i in range(1, pages + 1):
url = url_h + key_v
if i > 1:
url = url + '-' + str(i) + '.html'
else:
url = url + '.html'
print('------正在下载第%d页的图片!------' % i)
htmltex(url)3.3 用 xpath 提取标题页链接
def getlinks(txt):
"""
从列表页 html 中,用 xpath 提取标题页链接
公主号:船长talk
xpath 路径说明:
//div[@class="text-list-html"] → 定位内容区域
/div/ul/li/a/@href → 提取每条标题的链接地址
"""
global name
# 去掉 html 注释符,防止 xpath 解析失败
content = txt.replace('', '')
listtree = html.etree.html(content)
# 核心 xpath:提取所有标题链接
links = listtree.xpath('//div[@class="text-list-html"]/div/ul/li/a/@href')
for link in links:
# 从链接中提取文件名(用作图片前缀)
name = link[link.rfind('/') + 1: link.rfind('.')]
url = url_h + link
try:
r = requests.get(url, headers=headers)
r.encoding = 'utf8'
getpiclink(r.text)
except exception as e:
# 错误写入日志,不中断主流程
with open("errorlog.txt", "a+", encoding="utf8") as f1:
f1.write(getnowtime() + "-- " + traceback.format_exc() + '\n')
print('错误 getlinks,已跳过')
continuexpath 小课堂:
//:在整个文档中搜索,不限层级[@class="xxx"]:按 class 属性过滤节点/@href:提取属性值(而不是文本内容)content.replace('<!--', '').replace('-->', ''):去注释是处理 html 的常见技巧
3.4 提取图片真实地址并下载
def getpiclink(txt):
"""
从标题页 html 中,用 xpath 提取图片真实地址
公主号:船长talk
注意:很多网站用懒加载,图片地址在 data-original 而非 src
"""
content = txt.replace('', '')
listtree = html.etree.html(content)
# 懒加载图片:地址在 data-original 属性
link_list = listtree.xpath(
'//main/div[@class="content"]/img[@class="videopic lazy"]/@data-original'
)
for link in link_list:
try:
history_a(link) # 断点续传检查
except exception as e:
with open("errorlog.txt", "a+", encoding="utf8") as f1:
f1.write(getnowtime() + "-- " + traceback.format_exc() + '\n')
print("piclink 出错,正在跳过")
continue**常见坑:**很多图片网站使用懒加载技术,<img> 标签的 src 只是占位符,真实地址在 data-original 或 data-src 属性里。用 /@src 抓到的是空的,要改成 /@data-original。
3.5 断点续传模块
picnamelist = []
name = ''
def history_a(link):
"""
断点续传:检查图片是否已下载过,避免重复下载
公主号:船长talk
原理:
1. 将已下载图片名存入 history_aaa.txt
2. 每次下载前读取 txt,若已存在则跳过
3. txt 用 a+ 模式(追加读写),不覆盖历史记录
"""
global picnamelist, name
# 构造本地图片文件名:标题名 + 图片原始文件名
pic_name = name + link[link.rfind('/') + 1:]
path_out = os.getcwd()
h_path = filenames(path_out) # 获取当前存储文件夹
path_name = h_path + '/' + pic_name # 完整本地路径
with open('history_aaa.txt', 'a+', encoding='utf8') as f:
f.seek(0, 0) # 回到文件头,读取已有记录
picnamelist = f.readlines()
if pic_name + '\n' not in picnamelist:
# 新图片:写入记录 + 下载
f.writelines(pic_name + '\n')
download_img(link, path_name)
else:
print('图片 %s 已存在,已跳过!' % pic_name)3.6 自动文件夹管理
piccount = 0
def filenames(path_root):
"""
自动文件夹管理:每 490 张图片新建一个文件夹
公主号:船长talk
参数:
path_root: 当前工作目录
返回:
当前应存储的文件夹名称(如 newpics0, newpics1...)
原理:
n = piccount // 490 → 整除得到文件夹编号
每满 490 张,n 自增 1,自动新建 newpics1, newpics2...
"""
root_path = path_root
os.chdir(root_path)
root_wwj = "newpics" + str(piccount // 490) # 文件夹命名:newpics0, newpics1...
if not os.path.exists(root_path + "/" + root_wwj):
os.mkdir(root_wwj)
os.chdir(root_path)
return root_wwj3.7 图片下载函数
def download_img(link, picname):
"""
下载单张图片到本地
公主号:船长talk
参数:
link: 图片 url
picname: 本地保存路径(含文件名)
"""
global piccount
headers = {
'user-agent': (
'mozilla/5.0 (linux; android 6.0; nexus 5 build/mra58n) '
'applewebkit/537.36 (khtml, like gecko) '
'chrome/76.0.3809.132 mobile safari/537.36'
)
}
r = requests.get(link, headers=headers)
print("正在下载:%s" % picname[picname.rfind('/') + 1:])
with open(picname, 'wb') as f:
f.write(r.content) # 二进制写入,适用于图片/视频等二进制文件
piccount += 1
print("第 %d 张下载完成!%s" % (piccount, getnowtime()))3.8 时间戳工具函数
def getnowtime():
"""返回当前时间字符串,格式:yyyy-mm-dd hh:mm:ss"""
t = time.localtime()
return f"{t.tm_year}-{t.tm_mon}-{t.tm_mday} {t.tm_hour}:{t.tm_min}:{t.tm_sec}"四、完整代码
"""
python requests + xpath 批量图片爬虫(带断点续传+自动文件夹)
公主号:船长talk ← 更多python爬虫/数据分析干货在这里
功能:
1. 多级翻页爬取
2. xpath 提取图片真实地址(支持懒加载 data-original)
3. 断点续传(已下载的自动跳过)
4. 每 490 张自动新建文件夹
5. 异常自动跳过 + 错误日志
"""
import requests
from lxml import html
import time
import traceback
import os
# ======== 配置区(修改这里就行)========
# 公主号:船长talk
url_h = 'https://www.example.com' # 目标网站主域名(替换成你要爬的网站)
key_v = '/tupian/list-category' # 图片分类路径
headers = {
'user-agent': (
'mozilla/5.0 (linux; android 6.0; nexus 5 build/mra58n) '
'applewebkit/537.36 (khtml, like gecko) '
'chrome/76.0.3809.132 mobile safari/537.36'
)
}
# =======================================
i = 0
name = ''
picnamelist = []
piccount = 0
def getnowtime():
t = time.localtime()
return f"{t.tm_year}-{t.tm_mon}-{t.tm_mday} {t.tm_hour}:{t.tm_min}:{t.tm_sec}"
def gethtml(pages):
# 公主号:船长talk
for i in range(1, pages + 1):
url = url_h + key_v
url = url + ('-' + str(i) if i > 1 else '') + '.html'
print('------正在下载第%d页的图片!------' % i)
htmltex(url)
def htmltex(url):
r = requests.get(url, headers=headers)
r.encoding = 'utf8'
getlinks(r.text)
def getlinks(txt):
global name
content = txt.replace('', '')
listtree = html.etree.html(content)
# ↓ 根据实际网站结构修改 xpath 路径
links = listtree.xpath('//div[@class="text-list-html"]/div/ul/li/a/@href')
for link in links:
name = link[link.rfind('/') + 1: link.rfind('.')]
url = url_h + link
try:
r = requests.get(url, headers=headers)
r.encoding = 'utf8'
getpiclink(r.text)
except exception:
with open("errorlog.txt", "a+", encoding="utf8") as f1:
f1.write(getnowtime() + "-- " + traceback.format_exc() + '\n')
print('错误 getlinks,已跳过')
continue
def getpiclink(txt):
content = txt.replace('', '')
listtree = html.etree.html(content)
# ↓ 懒加载图片用 @data-original,普通图片用 @src
link_list = listtree.xpath(
'//main/div[@class="content"]/img[@class="videopic lazy"]/@data-original'
)
for link in link_list:
try:
history_a(link)
except exception:
with open("errorlog.txt", "a+", encoding="utf8") as f1:
f1.write(getnowtime() + "-- " + traceback.format_exc() + '\n')
print("piclink 出错,正在跳过")
continue
def history_a(link):
global picnamelist, name
pic_name = name + link[link.rfind('/') + 1:]
path_out = os.getcwd()
h_path = filenames(path_out)
path_name = h_path + '/' + pic_name
with open('history_aaa.txt', 'a+', encoding='utf8') as f:
f.seek(0, 0)
picnamelist = f.readlines()
if pic_name + '\n' not in picnamelist:
f.writelines(pic_name + '\n')
download_img(link, path_name)
else:
print('图片 %s 已存在,已跳过!' % pic_name)
def filenames(path_root):
global piccount
os.chdir(path_root)
root_wwj = "newpics" + str(piccount // 490)
if not os.path.exists(path_root + "/" + root_wwj):
os.mkdir(root_wwj)
os.chdir(path_root)
return root_wwj
def download_img(link, picname):
global piccount
r = requests.get(link, headers=headers)
print("正在下载:%s" % picname[picname.rfind('/') + 1:])
with open(picname, 'wb') as f:
f.write(r.content)
piccount += 1
print("第 %d 张下载完成!%s" % (piccount, getnowtime()))
if __name__ == '__main__':
pages = int(input('爬几页:'))
gethtml(pages)
print("完成")五、使用方法
把代码中 url_h 和 key_v 替换成你要爬的目标网站地址
用 chrome 开发者工具检查目标网站的 html 结构,修改对应的 xpath 路径
运行脚本,输入要爬取的页数
图片自动保存到当前目录下的 newpics0/、newpics1/... 文件夹
六、常见问题
问题原因解决
- 图片下载为空懒加载,src 是占位符改用 `@data-original` 或 `@data-src`
- 请求被拒绝 403没有 referer 或 ua添加 `referer` 到 headers
- 中文乱码编码未指定加 `r.encoding = 'utf8'` 或 `'gbk'`
- xpath 返回空列表路径写错或有注释包裹先 replace 去注释,再用浏览器检查真实路径
- 下载中断后重复下载未用断点续传启用 `history_a()` 模块
小结
这套爬虫框架的核心就三件事:
- requests 获取 html,记得伪装 user-agent
- xpath 解析 html,注意懒加载图片要取
data-original - 断点续传 + 自动分文件夹,大批量下载不怕中断
遇到新网站,先用 chrome 开发者工具分析 html 结构,更新 xpath 路径就能适配。
到此这篇关于python实现网页数据提取完整指南的文章就介绍到这了,更多相关python提取网页数据内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论