如何用Python快速撸出一个贴吧图片下载器

这是一篇关于自己写贴吧图片下载器的总结。
 
起初是室友说要做一个贴吧的信息收集爬虫,于是打算先从简单一点的开始,先做一个贴吧的图片下载吧,之后再把获取图片改成获取背的内容不就好了嘛😄,然后就开始写了这个爬虫。现在这个Python脚本主要用到了下面的一些东西。  

用到的Python模块

  • requests(Python下的HTTP客户端实现)
  • BeautifulSoup(一个很方便的html语法分析工具)
  • colorama(在控制台输出彩色的
  • threading(我用到了多线程的一些东西)
  • signal(Python下和信号相关的东西)
  • OptionParser(很方便的命令行参数配置工具)
  • threadpool(一个菊苣写的Python线程池实现)

原理的部分

其实思路很简单,随便找一个帖子,比如这个http://tieba.baidu.com/p/2545680017,这是一个图楼,然后就需要分析页面的HTM代码内容,我用的是Chrome浏览器,那就右键选择一个楼层的图片,审查元素,然后发现HTML代码中是这样写的:  

<img class="BDE_Image" src="http://imgsrc.baidu.com/forum/w%3D580/sign=198d73cadc54564ee565e43183dc9cde/857f1f1f95cad1c8f75fb3fe7e3e6709c83d5130.jpg" pic_ext="jpeg" width="550" height="600">

 
可以看到里面src部分直接是图片的地址,再观察楼层图片和其他无关紧要的图片(比如层主头像、签名档)的写法区别,发现楼层图片都会有一个BDE_Imageclass,那么这就好办了,等会直接筛选出来就好。

然后翻到帖子的第二页,发现URL变成了http://tieba.baidu.com/p/2545680017?pn=2,多了一个?pn=2,这个pn参数也就表示了当前查看的页数位置,那么我们只需要知道帖子总共有多少页,对pn做一个遍历就可以获得一个帖子中所有页面了,现在需要知道怎么获取帖子的总页面数。直接来到帖子的最底部,看到了这样的内容62回复贴,共3页,对应的HTML代码为:  

<li class="l_reply_num" style="margin-left:8px"><span class="red" style="margin-right:3px">62</span>回复贴,共<span class="red">3</span>页</li>

 
特征很明显,有一个叫做l_reply_num的class,总回帖数和总页数都各用一个sapn标签包起来了,等会直接过滤出来就可以。

现在对网页部分的分析算是结束了,到了代码实现的部分了。

这里贴出部分代码

# 用requests模块直接获取网页的HTML内容
getparams = {'pn': 1}
result_ = requests.get('http://tieba.baidu.com/p/2545680017?', params=getparams).content
# 用BeautifulSoup模块做HTML内容的分析,获取总页数
soup = BeautifulSoup(result_, 'lxml')
page_list = soup.find('li', attrs={'class': 'l_reply_num'})
max_page_number = int(page_list.contents[2].string)  # 总页数
# 在pn处做一个遍历,获取到帖子的所有页面
for pageNum in range(start, end):
    getparams = {'pn': pageNum}
    html_cont = requests.get(self.pURL, params=getparams).content
    soup = BeautifulSoup(html_cont, 'lxml')
    # 用BeautifulSoup模块做HTML内容的分析,获取到所有楼层图片的下载地址
    # 并发送到下载队列
    for item in soup.findAll('img', attrs={'class': "BDE_Image"}):
        srcurl = item.get('src')  # 图片地址
        if not self._is_Exit:
            self.downloader.add_task(srcurl)
# 下载器里用threadpool模块做多线程的并发下载,这样速度比单线程快很多
def add_task(self, url):
    with self.lock:
        self._is_alive = True
    reqs = threadpool.makeRequests(self._download, [url, ], self._callback)
    [self.threadPool.putRequest(req, timeout=1) for req in reqs]
# 直接用requests模块完成图片的下载,并保存到文件
def _download(self, url):
    if self.is_alive():
        r = requests.request('GET', url)
        if r:
            filename = os.path.join(self.save_directory, os.path.basename(url))  # 拼接文件名
            if not os.path.exists(filename):
                with open(filename, 'wb') as f:
                    f.write(r.content)  # 保存图片
                return 1
            else:
                return -1
    else:
        return None

速度还不错

 

END

到这里其实关键的部分也就结束了,剩下的一些微小的工作,比如输出统计结果(下了多少图,用了多少时间,支持设置保存目录。。。),各位看官感兴趣的话可以直接去我的Github上看源码。

Github https://github.com/zhaohui8969/tiebaImageGet