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编码/解码

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 分析关键信息

  1. 打开贴吧主页。点击右上角设置图标->更多工具->开发者工具(谷歌浏览器),或网页鼠标右键点击->检查即可看到源码。

谷歌浏览器打开源码方式

  1. Ctrl+F查找帖子编号 “6383722178” ,找到关键代码,提取出正则表达式。若提取内容不同,所需的正则表达式需要自己修改,不懂可以参考Python3 正则表达式
    注意:浏览器上看到的关键代码和程序获取的关键代码是不同的。

浏览器上帖子关键代码

程序获取的关键代码:

<li class=" j_thread_list clearfix" data-field='{&quot;id&quot;:6383722178,&quot;author_name&quot;:&quot;lovedovelyonly&quot;,&quot;author_nickname&quot;:null,&quot;author_portrait&quot;:&quot;tb.1.d0fa6477.jIFXO-fqudlDDQVl2lym9g&quot;,&quot;first_post_id&quot;:128733834249,&quot;reply_num&quot;:26,&quot;is_bakan&quot;:null,&quot;vid&quot;:&quot;&quot;,&quot;is_good&quot;:true,&quot;is_top&quot;:null,&quot;is_protal&quot;:null,&quot;is_membertop&quot;:null,&quot;is_multi_forum&quot;:null,&quot;frs_tpoint&quot;:null}'  data-tid='6383722178' data-thread-type="0" data-floor='6''>

程序所需的正则表达式:

r1 = r"data-tid='([0-9]+)' data-thread-type"  # 正则表达式
  1. 同样打开贴吧首页的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"'  # 正则表达式
  1. 打开原图显示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:百度贴吧首页下载完成,想换页怎么办?

孙允珠吧首页

孙允珠吧第2页

孙允珠吧第10页

解决方法:由上图可知,其它页的网址可以直接从首页推出。即第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库——下载网页、爬虫汇总

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐