1. 背景
百度贴吧上有许多美图,几张或十几张图片手动下载还可以,但几十张,上百张就比较麻烦,故希望编个程序批量下载百度贴吧的原图。由于python爬虫代码简单,故利用python来编程。
2. 准备
2.1 贴吧组成
现在使用孙允珠吧举例。一个贴吧由一个个帖子组成,帖子中包含图片。故理论上下载原图的一般方法是:先打开贴吧主页,再打开帖子,然后点击图片使其原图显示,最后下载图片。
2.2 分析网页url
贴吧主页:http://tieba.baidu.com/f?kw=%CB%EF%D4%CA%D6%E9&fr=ala0&loc=rec
贴吧帖子:http://tieba.baidu.com/p/6383722178
原图显示:http://tieba.baidu.com/photo/p?kw=%E5%AD%99%E5%85%81%E7%8F%A0&ie=utf-8&flux=1&tid=6383722178&pic_id=4254484e251f95ca67a500d0c6177f3e660952e7&pn=1&fp=2&see_lz=1
分析结果:
1. 贴吧主页中 “kw=%CB%EF%D4%CA%D6%E9” 是gb2312格式的UrlEncode编码,“kw=%E5%AD%99%E5%85%81%E7%8F%A0” 是utf-8格式的UrlEncode编码,它们解码结果是:孙允珠。
解码网站:站长工具UrlEncode编码/解码
2. 贴吧帖子中 “6383722178” 是帖子编号。
3. 原图显示中 “pic_id=4254484e251f95ca67a500d0c6177f3e660952e7” 是图片编号。
4. 由原图显示的url可知,打开1张原图需要 “kw=%E5%AD%99%E5%85%81%E7%8F%A0” 、“tid=6383722178” 和 “pic_id=4254484e251f95ca67a500d0c6177f3e660952e7”
2.3 分析关键信息
- 打开贴吧主页。点击右上角设置图标->更多工具->开发者工具(谷歌浏览器),或网页鼠标右键点击->检查即可看到源码。
- Ctrl+F查找帖子编号 “6383722178” ,找到关键代码,提取出正则表达式。若提取内容不同,所需的正则表达式需要自己修改,不懂可以参考Python3 正则表达式。
注意:浏览器上看到的关键代码和程序获取的关键代码是不同的。
程序获取的关键代码:
<li class=" j_thread_list clearfix" data-field='{"id":6383722178,"author_name":"lovedovelyonly","author_nickname":null,"author_portrait":"tb.1.d0fa6477.jIFXO-fqudlDDQVl2lym9g","first_post_id":128733834249,"reply_num":26,"is_bakan":null,"vid":"","is_good":true,"is_top":null,"is_protal":null,"is_membertop":null,"is_multi_forum":null,"frs_tpoint":null}' data-tid='6383722178' data-thread-type="0" data-floor='6''>
程序所需的正则表达式:
r1 = r"data-tid='([0-9]+)' data-thread-type" # 正则表达式
- 同样打开贴吧首页的1个帖子,Ctrl+F查找图片编号 “4254484e251f95ca67a500d0c6177f3e660952e7”,获取关键代码,提取出正则表达式。
程序获取的关键代码:
<img class="BDE_Image" pic_type="0" width="560" height="560" src="http://imgsrc.baidu.com/forum/w%3D580/sign=61a7dc9d0546f21fc9345e5bc6256b31/4254484e251f95ca67a500d0c6177f3e660952e7.jpg" size="555815" >
程序所需的正则表达式:
r2 = r'src="http://imgsrc.baidu.com/(.*?)\.jpg"' # 正则表达式
- 打开原图显示url的源码,找出可下载原图的地址。
可下载原图的地址:
http://imgsrc.baidu.com/forum/pic/item/4254484e251f95ca67a500d0c6177f3e660952e7.jpg
三、处理流程
从上面的下载地址可知,下载原图需要知道 “4254484e251f95ca67a500d0c6177f3e660952e7” 即图片编号。故批量下载图片时,只需给定贴吧首页的url,由首页url的源码找出帖子编号,再由帖子编号读取帖子源码,从而获取图片编号。最后由图片编号组成原图下载地址,通过其下载图片。
四、源代码
from urllib import request # 下载图片
import re # 正则表达式
import os # 创建文件夹
from tqdm import trange # 进度条
# 读取主页源码
url1 = "http://tieba.baidu.com/f?kw=%CB%EF%D4%CA%D6%E9&fr=ala0&loc=rec" # 贴吧首页的网址
html = request.urlopen(url1, timeout=5) # 读取网页源码
code = html.read().decode("UTF_8")
"""
file = open("1.html", "w", encoding='UTF-8') # 保存源码
file.write(str(code))
file.close
print(code)
"""
# 提取帖子编号
r1 = r"data-tid='([0-9]+)' data-thread-type" # 正则表达式
c1 = re.compile(r1) # 根据正则表达式创建模式对象
f1 = re.findall(c1, code) # 匹配结果
# 提取帖子名称
r2 = r'href="/p/[0-9]+" title="(.*?)"' # 正则表达式
c2 = re.compile(r2) # 根据正则表达式创建模式对象
title = re.findall(c2, code) # 匹配结果
i = 0
# 创建Image文件夹
if not os.path.exists("Image"):
os.mkdir("Image")
for tid in f1:
i = i + 1
# 读取帖子源码
url3 = 'http://tieba.baidu.com/p/' + tid # 帖子地址 'http://tieba.baidu.com/p/6383722178'
html3 = request.urlopen(url3, timeout=5) # 读取网页源码
code3 = html3.read().decode("UTF_8")
# 提取图片编号
r3 = r'src="http://imgsrc.baidu.com/(.*?)\.jpg"' # 正则表达式
c3 = re.compile(r3) # 根据正则表达式创建模式对象
f3 = re.findall(c3, code3) # 匹配结果
# 以该帖子的名称创建文件夹
dir1 = "Image//" + title[i] + "(共" + str(len(f3)) + "张)//"
if not os.path.exists(dir1):
os.mkdir(dir1) # 创建帖子文件夹
with trange(len(f3)) as bar: # 进度条
for j in bar:
# 下载图片并保存
s = f3[j].split("/")
pic_id = s[len(s) - 1]
# 图片下载地址'http://imgsrc.baidu.com/forum/pic/item/4254484e251f95ca67a500d0c6177f3e660952e7.jpg'
url4 = 'http://imgsrc.baidu.com/forum/pic/item/' + pic_id + '.jpg'
f = open(dir1 + str(j) + ".jpg", 'wb')
f.write((request.urlopen(url4, timeout=5)).read())
f.close()
# 打印进度信息
bar.set_description(str(i) + ". " + title[i] + "(共" + str(len(f3)) + "张)")
print("贴吧首页下载完成。")
五、遇到的问题
问题1:点击程序运行,但程序一直卡住,既不报错,又没有结果。
原因:经调试发现程序一直卡住code = html.read(),所以程序应加上超时处理和异常处理。如此程序会正常报错。
报错程序:
from urllib import request
html = urllib.request.urlopen(url)
code = html.read()
code = code.decode("UTF_8")
改正后程序
from urllib import request, error
try:
html = request.urlopen(url, timeout=3)
code = html.read()
code = code.decode("UTF_8")
except error.URLError as e:
print(e.reason)
问题2:点击程序运行,发出超时错误。
原因:这可能是网络连接不佳或者网站发现是爬虫程序而禁止访问,推荐博客python urllib.request的5个基本程序,使用异常处理,User_Agent和IP代理运行测试一下是否可以读取url的源码。
问题3:百度贴吧首页下载完成,想换页怎么办?
解决方法:由上图可知,其它页的网址可以直接从首页推出。即第n页的url
="http://tieba.baidu.com/f?kw=%E5%AD%99%E5%85%81%E7%8F%A0&ie=utf-8&pn="+(n-1)*50
问题4:在百度贴吧首页查找帖子编号 “6383722178”和图片编号 “4254484e251f95ca67a500d0c6177f3e660952e7”也能找到,为何还要打开具体帖子读取图片编号?
解答:百度贴吧首页获取的图片编号只是帖子中图片编号的一部分而已,它并不完全。
问题5:在具体帖子的源码中已经可以找到所有图片编号以及地址,为何不用这个地址,而要使用原图显示图片的地址?
解答:直接使用帖子中的图片地址也是可以下载图片的,但它只是按比例缩小的缩略图,不是原图。
六、参考资料
1、Python学习笔记(二)urllib.urlopen()超时问题 : 504Gateway Time-out
2、【Python】urllib库——下载网页、爬虫汇总