Python爬虫初学(二)—— 爬百度贴吧
Python爬虫初学(二)—— 爬百度贴吧
昨天初步接触了爬虫,实现了爬取网络段子并逐条阅读等功能,详见Python爬虫初学(一)。 今天准备对百度贴吧下手了,嘿嘿。依然是跟着这个博客学习的,这次仿照该博主用类的方式写。
其实我从来不玩贴吧,不过据我所知贴吧有一些网友,他们开帖子连载原创小说;还有些网友提供“福利”,造福广大网民。嗯,所以今天的目标是这样的:
- 把分散的连载小说下载到本地
- 批量下载贴吧图片
一. 下载小说
1. 定义一个类
这次用类来写。实现这个也不难,经过昨天的学习已经有一定经验了。导入库什么的就不说了。先看贴吧的url构成,如http://tieba.baidu.com/p/4723863270?see_lz=1&pn=2
。其中http://tieba.baidu.com/p/4723863270
为该帖的基础地址,?see_lz=1
是只看楼主标志位,为1
是表示“只看楼主”,pn=2
代表当前帖子的页码。现在来定义一个爬取百度贴吧的SpiderBaidu
,初始化,然后定义一个open_url()
来返回网页内容。
class SpiderBaidu:
# 初始化帖子原地址,默认只看楼主
def __init__(self, url, see_lz_flag=1):
self.url = url
# 可设置看所有楼
self.see_lz = '?see_lz=' + str(see_lz_flag)
self.res = []
# 打开具体网址并返回网页内容
def open_url(self, num):
# 该帖具体网址,num指定页码
wanted_page = self.url + self.see_lz + '&pn=' + str(num)
req = request.Request(wanted_page)
req.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 '
'(KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36')
response = request.urlopen(req)
html = response.read().decode('utf-8')
return html
2. 获取标题和总页数
我们想要知道帖子标题以及总页数,提取出来就是了!
可以发现标题被<h1 class="core_title_txt(...)</h1>
包含起来了,这里要注意的是,有些帖子不是<h1>
,可能是<h3>
或者其他,一会儿匹配的时候考虑进去。
# 提取标题
def get_title(self):
# 第1页就包含标题,所以num用1即可
html = self.open_url(1)
# 提取标题的规则,<h\d>即无论h1还是h3都会匹配成功
title_pattern = re.compile(r'<h\d class="core_title_txt.*?>(.*?)</h\d>')
title = re.findall(title_pattern, html)
# 返回的是列表且只有一个元素,故用title[0]
return title[0]
接下来是总页数,仔细观察总页数其实在最上面和最下面都是有一个的,所以一会儿匹配后返回的列表会有两个元素,这两个元素是一模一样的!
如上图,数字7被<span class="red">(需要提取的数字)</span>
包含。代码如下。
# 获取总页数
def get_page_num(self):
# 第1页也有总页数
html = self.open_url(1)
num_pattern = re.compile(r'<span class="red">(\d+)</span>')
page_num = re.findall(num_pattern, html)
# 贴吧的最上和最下面都有总页码,随便返回一个即可
return page_num[0]
我们来看一下提取出来的标题和页码。
3. 获取正文
正文前面有空格,依然要用\s+
匹配。正文被<div id="post_content...class=d_post_content j_d_post_content...空格空格(正文)</div>
包含。以下函数提取出正文。
# 获取正文
def get_content(self, num):
# 获取网页全部内容
html = self.open_url(num)
# 提取每楼发言
content_pattern = re.compile(r'<div id="post_content.*?class="d_post_content j_d_post_content'
r'.*?>\s+(.*?)</div>')
content = re.findall(content_pattern, html)
return content
即使是提取出帖子正文了,也别高兴的太早。贴吧发帖不可能人人都发的纯文本,可以预想到里面会有图片(包含表情),超链接,还有设置的签名等。这些还没有被过滤掉。(不好意思忘了截图,反正打印出来的内容会含有很多又长又难看的链接)
我们再制定规则过滤掉。
# 这里参数con为get_content()函数返回的包含正文的列表
def get_words_only(self, con):
for i in con:
# 删除图片
each = re.sub(r'<img class=".*?>', '', i)
# 删除签名
each = re.sub(r'<div class="post_bubble_top".*?>', '', each)
# 换行
each = re.sub(r'<br>', '\n', each)
# 删除超链接
each = re.sub(r'<a href=.*?</a>', '', each)
# 添加到初始化的列表中
self.res.append(each)
return self.res
4. 下载小说到本地
默认模式为只看楼主,其他人插楼小说还咋读是不。
# 下载到本地
def save_text(self):
# 返回的帖子标题作为文件名
file_title = self.get_title()
# 最大页码
page_num = int(self.get_page_num())
with open(file_title + '.txt', 'w', encoding='utf-8') as f:
# 每一页内容都写入文件
for number in range(1, page_num + 1):
con = self.get_content(number)
# 只留下纯文字,过滤图片、超链接等
result = self.get_words_only(con)
f.writelines(result)
最后创建一个实例就好了,试试下载吧。
if __name__ == '__main__':
spider = SpiderBaidu('http://tieba.baidu.com/p/4698209454')
title = spider.get_title()
total_num = spider.get_page_num()
print('{}(共{}页)'.format(title, total_num))
spider.save_text()
下载下来后是这个效果,还行,能读。
二、 批量下载图片
刚才有过滤图片是不?我们反过来利用它,分分钟就搞定!
提取图片链接即可。它被<img class="BDE_Image" src="(.*?jpg)"
这样的形式包含。
# 只保存图片
def save_images(self, folder):
page_num = int(self.get_page_num())
# 文件名序号
seq = 1
# 创建文件夹
os.mkdir(folder)
# 工程目录切换到当成文件夹
os.chdir(folder)
for number in range(1, page_num + 1):
# 网页全部内容
html = self.open_url(number)
img_pattern = re.compile(r'<img class="BDE_Image" src="(.*?jpg)"')
images = re.findall(img_pattern, html)
# 每爬一页,休息10秒
time.sleep(10)
for each in images:
# 文件名
filename = str(seq) + '.jpg'
# 下载到文件夹
request.urlretrieve(each, filename)
# 数字递增方式给文件命名
seq += 1
# 每两秒下载一次
time.sleep(2)
可以适当加入
time.sleep()
,防止访问频率过快导致爬虫封IP。简单的可以这么做,当然可以用代理,多线程,不过我还没接触到,以后再深入。
居然几百张!大丰收呀,看到图片自动地就被飞速下载到本地了,还用一张张右键吗?No!挂着程序让它跑,看部电影去吧!
整理一下,全部代码如下
from urllib import request, parse
import re
import os
import time
class SpiderBaidu:
# 初始化帖子原地址,默认只看楼主为否
def __init__(self, url, see_lz_flag=1):
self.url = url
self.see_lz = '?see_lz=' + str(see_lz_flag)
self.res = []
def open_url(self, num):
# 该帖具体网址
wanted_page = self.url + self.see_lz + '&pn=' + str(num)
req = request.Request(wanted_page)
req.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 '
'(KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36')
response = request.urlopen(req)
html = response.read().decode('utf-8')
return html
# 提取标题
def get_title(self):
html = self.open_url(1)
title_pattern = re.compile(r'<h\d class="core_title_txt.*?>(.*?)</h\d>')
title = re.findall(title_pattern, html)
return title[0]
# 获取总页数
def get_page_num(self):
html = self.open_url(1)
num_pattern = re.compile(r'<span class="red">(\d+)</span>')
page_num = re.findall(num_pattern, html)
# 贴吧的最上和最下面都有总页码,随便返回一个即可
return page_num[0]
# 获取正文
def get_content(self, num):
html = self.open_url(num)
content_pattern = re.compile(r'<div id="post_content.*?class="d_post_content j_d_post_content'
r'.*?>\s+(.*?)</div>')
content = re.findall(content_pattern, html)
return content
# 去除文字外所有内容
def get_words_only(self, con):
for i in con:
# 删除图片
each = re.sub(r'<img class=".*?>', '', i)
# 删除签名
each = re.sub(r'<div class="post_bubble_top".*?>', '', each)
# 换行
each = re.sub(r'<br>', '\n', each)
# 删除超链接
each = re.sub(r'<a href=.*?</a>', '', each)
self.res.append(each)
return self.res
# 下载到本地
def save_text(self):
# 帖子标题作为文件名
file_title = self.get_title()
# 最大页码
page_num = int(self.get_page_num())
with open(file_title + '.txt', 'w', encoding='utf-8') as f:
for number in range(1, page_num + 1):
con = self.get_content(number)
result = self.get_words_only(con)
f.writelines(result)
# 只保存图片
def save_images(self, folder):
page_num = int(self.get_page_num())
# 文件名序号
seq = 1
os.mkdir(folder)
os.chdir(folder)
for number in range(1, page_num + 1):
html = self.open_url(number)
img_pattern = re.compile(r'<img class="BDE_Image" src="(.*?jpg)"')
images = re.findall(img_pattern, html)
time.sleep(10)
for each in images:
filename = str(seq) + '.jpg'
request.urlretrieve(each, filename)
seq += 1
time.sleep(2)
if __name__ == '__main__':
spider = SpiderBaidu('http://tieba.baidu.com/p/4698209454')
title = spider.get_title()
total_num = spider.get_page_num()
print('{}(共{}页)'.format(title, total_num))
spider.save_text()
spider.save_images('图')
by @sunhaiyu
2016.8.16
Python爬虫初学(二)—— 爬百度贴吧的更多相关文章
- Python 爬虫入门(二)——爬取妹子图
Python 爬虫入门 听说你写代码没动力?本文就给你动力,爬取妹子图.如果这也没动力那就没救了. GitHub 地址: https://github.com/injetlee/Python/blob ...
- Python爬虫学习(二) ——————爬取前程无忧招聘信息并写入excel
作为一名Pythoner,相信大家对Python的就业前景或多或少会有一些关注.索性我们就写一个爬虫去获取一些我们需要的信息,今天我们要爬取的是前程无忧!说干就干!进入到前程无忧的官网,输入关键字&q ...
- Python爬虫实战二之爬取百度贴吧帖子
大家好,上次我们实验了爬取了糗事百科的段子,那么这次我们来尝试一下爬取百度贴吧的帖子.与上一篇不同的是,这次我们需要用到文件的相关操作. 前言 亲爱的们,教程比较旧了,百度贴吧页面可能改版,可能代码不 ...
- 转 Python爬虫实战二之爬取百度贴吧帖子
静觅 » Python爬虫实战二之爬取百度贴吧帖子 大家好,上次我们实验了爬取了糗事百科的段子,那么这次我们来尝试一下爬取百度贴吧的帖子.与上一篇不同的是,这次我们需要用到文件的相关操作. 本篇目标 ...
- Python爬虫实战之爬取百度贴吧帖子
大家好,上次我们实验了爬取了糗事百科的段子,那么这次我们来尝试一下爬取百度贴吧的帖子.与上一篇不同的是,这次我们需要用到文件的相关操作. 本篇目标 对百度贴吧的任意帖子进行抓取 指定是否只抓取楼主发帖 ...
- 【转载】教你分分钟学会用python爬虫框架Scrapy爬取心目中的女神
原文:教你分分钟学会用python爬虫框架Scrapy爬取心目中的女神 本博文将带领你从入门到精通爬虫框架Scrapy,最终具备爬取任何网页的数据的能力.本文以校花网为例进行爬取,校花网:http:/ ...
- 2.Python爬虫入门二之爬虫基础了解
1.什么是爬虫 爬虫,即网络爬虫,大家可以理解为在网络上爬行的一直蜘蛛,互联网就比作一张大网,而爬虫便是在这张网上爬来爬去的蜘蛛咯,如果它遇到资源,那么它就会抓取下来.想抓取什么?这个由你来控制它咯. ...
- Python爬虫入门二之爬虫基础了解
1.什么是爬虫 爬虫,即网络爬虫,大家可以理解为在网络上爬行的一直蜘蛛,互联网就比作一张大网,而爬虫便是在这张网上爬来爬去的蜘蛛咯,如果它遇到资源,那么它就会抓取下来.想抓取什么?这个由你来控制它咯. ...
- 转 Python爬虫入门二之爬虫基础了解
静觅 » Python爬虫入门二之爬虫基础了解 2.浏览网页的过程 在用户浏览网页的过程中,我们可能会看到许多好看的图片,比如 http://image.baidu.com/ ,我们会看到几张的图片以 ...
随机推荐
- 关于jQuery表单下拉selected设置无效的解决方案
表单下拉选项使用selected设置,发现第一次默认选中成功,在页面不刷新的情况下,再次下拉,selected属性设置了,默认选中不生效 研究发现是浏览器兼容的问题 一,解决浏览器缓存问题,在HTML ...
- CSS中的尺寸单位
绝对单位 px: Pixel 像素 pt: Points 磅 pc: Picas 派卡 in: Inches 英寸 mm: Millimeter 毫米 cm: Centimeter 厘米 q: Qua ...
- Python os模块实例之遍历目录及子目录指定扩展名的文件
需求:在该目录下有很多子目录(如下图,截图了部分),现要从该目录和所有子目录下找到所有扩展名为.meta的文件,并获取文件中第二行guid的值(': '后面的),然后将所有guid的值输出到另一文件中 ...
- Ubuntu16.04 + caffe-ssd + [CPU_ONLY] + KITTI 训练总结
本次训练主要参考:http://blog.csdn.net/jesse_mx/article/details/65634482 感谢 Jesse_Mx ,帮助了我很多. 坑一[openCV未安装成功] ...
- java 二叉树实现
//二叉树,有左子数,右子树,小的值放到左边,大的值放到右边,左边有树的话继续这个过程,(以下为代部分,赋值的时候采用了递归的方式) package liaobiao;class BiTree{ pr ...
- phpcms和php格式化时间戳
用PHPCMS V9 建站时,经常会用到时间标签,它是通用标签调用-日期时间格式化,适用全站. 一.日期时间格式化显示: a\标准型:{date('Y-m-d H:i:s', $rs['inputti ...
- oracle表空间增长异常或表空间占用过高问题分析
本人对oracle调优还处在不断学习状态,这个问题是之前处理的项目上遇到过的,顺利解决了,分享下此类问题的处理思路,不足之处,还请指正. 项目上反馈说业务表空间增长越来越快,上次新增的30G数据文件, ...
- PHP二维数组合并,根据某个key合并
/** *PHP二维数组合并,根据某个key合并 * @param type $v 要传入的组合阵列 * @param type $keyname 对应的KEY名称. * @param type $v ...
- 框架基础:ajax设计方案(五)--- 集成promise规范,更优雅的书写代码
距离上一篇博客书写,又过去了大概几个月了,这段时间暂时离开了这个行业,让大脑休息一下.一个人旅行,一个人休息,正好也去完成一个目标 --- 拥有自己的驾照.当然,也把自己晒的黑漆马虎的.不过这一段时间 ...
- WCF学习——构建第二个WCF应用程序(六)
一.创建客户端应用程序 若要创建客户端应用程序,你将另外添加一个项目,添加对该项目的服务引用,配置数据源,并创建一个用户界面以显示服务中的数据.若要创建客户端应用程序,你将另外添加一个项目,添加对该项 ...