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爬虫初学(二)—— 爬百度贴吧的更多相关文章

  1. Python 爬虫入门(二)——爬取妹子图

    Python 爬虫入门 听说你写代码没动力?本文就给你动力,爬取妹子图.如果这也没动力那就没救了. GitHub 地址: https://github.com/injetlee/Python/blob ...

  2. Python爬虫学习(二) ——————爬取前程无忧招聘信息并写入excel

    作为一名Pythoner,相信大家对Python的就业前景或多或少会有一些关注.索性我们就写一个爬虫去获取一些我们需要的信息,今天我们要爬取的是前程无忧!说干就干!进入到前程无忧的官网,输入关键字&q ...

  3. Python爬虫实战二之爬取百度贴吧帖子

    大家好,上次我们实验了爬取了糗事百科的段子,那么这次我们来尝试一下爬取百度贴吧的帖子.与上一篇不同的是,这次我们需要用到文件的相关操作. 前言 亲爱的们,教程比较旧了,百度贴吧页面可能改版,可能代码不 ...

  4. 转 Python爬虫实战二之爬取百度贴吧帖子

    静觅 » Python爬虫实战二之爬取百度贴吧帖子 大家好,上次我们实验了爬取了糗事百科的段子,那么这次我们来尝试一下爬取百度贴吧的帖子.与上一篇不同的是,这次我们需要用到文件的相关操作. 本篇目标 ...

  5. Python爬虫实战之爬取百度贴吧帖子

    大家好,上次我们实验了爬取了糗事百科的段子,那么这次我们来尝试一下爬取百度贴吧的帖子.与上一篇不同的是,这次我们需要用到文件的相关操作. 本篇目标 对百度贴吧的任意帖子进行抓取 指定是否只抓取楼主发帖 ...

  6. 【转载】教你分分钟学会用python爬虫框架Scrapy爬取心目中的女神

    原文:教你分分钟学会用python爬虫框架Scrapy爬取心目中的女神 本博文将带领你从入门到精通爬虫框架Scrapy,最终具备爬取任何网页的数据的能力.本文以校花网为例进行爬取,校花网:http:/ ...

  7. 2.Python爬虫入门二之爬虫基础了解

    1.什么是爬虫 爬虫,即网络爬虫,大家可以理解为在网络上爬行的一直蜘蛛,互联网就比作一张大网,而爬虫便是在这张网上爬来爬去的蜘蛛咯,如果它遇到资源,那么它就会抓取下来.想抓取什么?这个由你来控制它咯. ...

  8. Python爬虫入门二之爬虫基础了解

    1.什么是爬虫 爬虫,即网络爬虫,大家可以理解为在网络上爬行的一直蜘蛛,互联网就比作一张大网,而爬虫便是在这张网上爬来爬去的蜘蛛咯,如果它遇到资源,那么它就会抓取下来.想抓取什么?这个由你来控制它咯. ...

  9. 转 Python爬虫入门二之爬虫基础了解

    静觅 » Python爬虫入门二之爬虫基础了解 2.浏览网页的过程 在用户浏览网页的过程中,我们可能会看到许多好看的图片,比如 http://image.baidu.com/ ,我们会看到几张的图片以 ...

随机推荐

  1. Ubuntu16.04更换漂亮绚丽flatabulous主题

    作者:tongqingliu 转载请注明出处: Ubuntu16.04更换漂亮绚丽flatabulous主题 更新 sudo apt-get update sudo apt-get upgrade 安 ...

  2. [HNOI2009]梦幻布丁 算法技巧之邻接链

    题目描述 N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色.例如颜色分别为1,2,2,1的四个布丁一共有3段颜色. 输入输出格式 输入格式: 第 ...

  3. arcgis api for js入门开发系列十一地图统计图

    上一篇实现了demo的叠加SHP图层,本篇新增地图统计图,截图如下: 地图统计图实现的思路如下:利用拓展arcgis api的js文件(MapChartGraphic.js以及MapChartGrap ...

  4. 使用zabbix_agent监控第一台linux服务器

    添加zabbix用户和组 groupadd zabbix useradd -g zabbix zabbix 解压并编译安装 tar xf zabbix-3.2.1.tar.gz cd zabbix-3 ...

  5. codeM美团编程大赛初赛B轮D题

    [编程题] 模 时间限制:1秒空间限制:32768K 给定四个正整数a,b,c,k,回答是否存在一个正整数n,使得a*n在k进制表示下的各位的数值之和模b为c.输入描述:第一行一个整数T(T < ...

  6. 打开vmvare出现The VMware Authorization Service is not running。

    win+r再输入cmd打开doc,输入services.msc打开服务,吧Vmware Authorization Service 更改为自动或者将其启动即可.

  7. 通过线程监控socket服务器是否done机

    现实中的socket可能会因为各种原因done机,但这么重要的服务器怎么能允许这种事情发生?这次我们就来通过一个线程去监控socket服务器,如果done机重新将其启动. 下面是监控项目和socket ...

  8. PAT-1099(Build A Binary Search Tree)

    题目见这里 分析:分四步进行 1)根据给定的结点情况建二叉树  2)对输入的键值排序(asending) 3)对二叉树中序遍历,同时对应赋key值 4)层次遍历(队列应用) 题目并不困难,但是我误入了 ...

  9. Day2_数字类型_字符串类型_列表类型_元组_字典_集合_字符编码_文件处理

    数字类型: 作用:年纪,等级,薪资,身份证号等: 10进制转为2进制,利用bin来执行. 10进制转为8进制,利用oct来执行. 10进制转为16进制,利用hex来执行. #整型age=10 prin ...

  10. 关于Win7 内存变小处理方法

    windows + R 输入msconfig 点击引导 点击高级选项 点击最大内存打钩,就好了,你重启,你的内存将恢复成原来的.