Py学习  »  Python

Python 爬虫小课 1-9 宝妈程序媛福利-育儿网问答数据抓取

梦想橡皮擦 • 4 年前 • 1033 次点击  

爬虫百例专栏连载已经结束,欢迎订阅 🙈100 篇爬虫文章合计 29.9 元,每篇只需 2.9 毛钱 🙈

🌹 橡皮擦 叨叨 🌹
本专栏为爬虫小课系列,周更 3+篇,专栏合计 9 篇文章,本专栏所有案例都将采用 requests 库编写,通过 9 个案例,让你深入理解 requests 库。
以上就是本系列专栏的核心目标。

本系列课程需要一定的 Python 语法基础,数据匹配将采用 Python 自带的 re 模块,故对正则表达式有一定的基础要求。
关于爬取环境的需求,Python3.5 以上版本,安装 requests 模块。

爬虫前的分析

类别页面分析

本次爬虫小课要爬取的的网站为育儿网( http://ask.ci123.com/ ) 的问答模块,我们要采集一下红框内的资料。

Python 爬虫小课 1-12 宝妈程序媛福利-育儿网问答数据抓取

对于该网站涉及的问题类型非常多,具体分类可以通过上述链接左侧的菜单获取到。如下图所示区域:

Python 爬虫小课 1-12 宝妈程序媛福利-育儿网问答数据抓取

在这里需要略微分析一下,分类地址的规律,如果没有规律,那第一步就先获取一下所有的分类地址,鼠标点击各链接发现,分类列表页链接如下:

http://ask.ci123.com/categories/show/2
http://ask.ci123.com/categories/show/3
http://ask.ci123.com/categories/show/4
http://ask.ci123.com/categories/show/{类别ID}
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

在这里先不要下结论,说 ID 是依次递增的,写爬虫程序如果过早的假定一定的规则,很容易出现数据丢失的情况,所以尽量都尝试一遍。

在这里也可以直接通过查看网页源码,看一下所有的地址,当然看完之后还是为了我们可以爬取到。最终查阅到所有的地址都为 http://ask.ci123.com/categories/show/{类别ID} 形式,只是最后的类别 ID 不是连续的。到这里问题分类分析完毕。

Python 爬虫小课 1-12 宝妈程序媛福利-育儿网问答数据抓取

问题列表页面分析

下面需要寻找列表页相关规律,点击任意类别之后,可以查阅到,页面数据样式都如下图所示:

Python 爬虫小课 1-12 宝妈程序媛福利-育儿网问答数据抓取

首先要做的第一件事请,就是查找分页规律,找到分页区域,鼠标依次点击分页,获取不同的分页地址。

Python 爬虫小课 1-12 宝妈程序媛福利-育儿网问答数据抓取

最后找到其规律链接地址如下:

http://ask.ci123.com/categories/show/4/all?p={页码}
  • 1
  • 1

有页码规律还不够,还需要找到末页,在源码中简单检索,找到末页对应的页码即可。

Python 爬虫小课 1-12 宝妈程序媛福利-育儿网问答数据抓取

到此爬虫前的分析分析完毕了,下面开始进行爬虫逻辑编码环节,也就是整理自己的思路。

逻辑编码(伪代码)

育儿网爬虫分为如下步骤:

  1. 通过 http://ask.ci123.com/ 页面,获取所有的分类页面地址
  2. 循环所有的分类页面地址
  3. 获取每个分类对应的列表页面,并获取总页码
  4. 从一开始循环到总页码
  5. 上一步循环过程中过去每一页待爬取的数据

思路整理完毕,编码环节其实就是一个简单的实现过程。

爬虫正式编码

request 库 get 方法说明

对于 requests 库来说,导入并快速应用是非常容易的,先通过抓取分类页面源码来看一下基本使用。

import requests

url = "http://ask.ci123.com/"

# 抓取分类页面
def get_category():
    res = requests.get("http://ask.ci123.com/")
    print(res.text)


if __name__ == "__main__":
    get_category()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

以上代码中最核心的方法就是 requests.get()了,该方法为 requests 模块通过 get 方式获取网站源码,该方法中的参数说明如下:

必选参数 url

requests.get(url="http://ask.ci123.com/")

  • 1
  • 2
  • 1
  • 2

传递 URL 参数

通过该参数可以构造出如下格式 https://www.baidu.com/s?wd=你好&rsv_spt=1&rsv_iqid=0x8dd347e100002e04
格式如下:

import requests
payload = {'key1':


    
 'value1', 'key2': 'value2'}
res = requests.get(url="http://ask.ci123.com/", params=payload)
print(res.url)
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

key1 为键名,value1 为键值。

定制请求头

在爬虫爬取的过程中,我们将尽量将爬虫模拟成真实的用户通过浏览器访问网站,所以很多时候需要定制浏览器请求头。格式如下:

import requests
payload = {'key1': 'value1', 'key2': 'value2'}
headers = {
    'user-agent': 'Baiduspider-image+(+http://www.baidu.com/search/spider.htm)'
}
res = requests.get(url="http://ask.ci123.com/",
                   params=payload, headers=headers)
print(res.url)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

其中 headers 中可以配置更多的内容,本篇博客不做展开,只需要先记住 headers 参数即可。

Cookie

Cookie 在很多爬虫程序中属于必备内容,这里有时会存储加密信息,有时会存储用户信息,格式如下:

import requests
payload = {'key1': 'value1', 'key2': 'value2'}
headers = {
    'user-agent': 'Baiduspider-image+(+http://www.baidu.com/search/spider.htm)'
}
cookies = dict(my_cookies='nodream')
res = requests.get(url="http://ask.ci123.com/",
                   params=payload, headers=headers, cookies=cookies)
print(res.text)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

禁用重定向处理

有些网站会携带重定向源码,在爬取的时候需要禁止网格员自动跳转,代码如下:

r = requests.get('http://github.com', allow_redirects=False)
  • 1
  • 1

超时

对于一个网络请求,有时会出现无法请求到的情况,这部分在官方手册高级部分有相应的说明,不过对于初学者可以先进行忽略超时的高级用法。

为防止服务器不能及时响应,大部分发至外部服务器的请求都应该带着 timeout 参数。在默认情况下,除非显式指定了 timeout 值,requests 是不会自动进行超时处理的。如果没有 timeout,你的代码可能会挂起若干分钟甚至更长时间。

常规代码如下:

import requests
payload = {'key1': 'value1', 'key2': 'value2'}
headers = {
    'user-agent': 'Baiduspider-image+(+http://www.baidu.com/search/spider.htm)'
}
cookies = dict(my_cookies='nodream')
res =


    
 requests.get(url="http://ask.ci123.com/",
                   params=payload, headers=headers, cookies=cookies, timeout=3)
print(res.text)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

高级部分参数

对于 get 方法,还有一些参数,在后续的博客中我们可能会用到,例如:

  • SSL 证书验证 (verify)
  • 客户端证书(cert)
  • 事件钩子(hooks)
  • 自定义身份验证(auth)
  • 流式请求(stream)
  • 代理(proxies)

以上参数都会出现在 get 方法中,所以 requests 库是一个非常非常强大的库。

获取所有的分类页面地址

有了上述详细的说明,在使用 requests 库去获取网页中的内容就变得简单一些了。这里需要有 Python 基础知识中 re 模块的使用与正则表达式的基础。具体的爬取代码如下:

import requests
import re

url = "http://ask.ci123.com/"
headers = {
    'user-agent': 'Baiduspider-image+(+http://www.baidu.com/search/spider.htm)'
}
# 抓取分类页面
def get_category():
    res = requests.get("http://ask.ci123.com/", headers=headers)
    pattern = re.compile(
        r'<li><a href="/categories/show/(\d+)">', re.S)
    categories_ids = pattern.findall(res.text)
    print(f"获取到的分类ID如下:",categories_ids)

if __name__ == "__main__":
    get_category()

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

循环所有的分类页面地址

在上述代码中通过 re库 的 findall 方法获取了所有的分类编号,用来拼接后续的待爬取页面。获取到 IDS 之后,就可以通过循环的方式获取到所有的列表页面了,具体如下:

# 抓取分类页面
def get_category():
    res = requests.get("http://ask.ci123.com/", headers=headers)
    pattern = re.compile(
        r'<li><a href="/categories/show/(\d+)">', re.S)
    categories_ids = pattern.findall(res.text)
    print(f"获取到的分类ID如下:", categories_ids)
    for cate in categories_ids:
		# 下述代码中有get_list()函数对应的代码
        get_list(cate)
        time.sleep(1)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

上述代码为了防止被反爬,需要增加一个延时处理, time.sleep()

获取每个分类对应的列表页面,并获取总页码

打开列表页面,首要目的先获取到总页码,本次实现的案例获取的页码途径比较简单,在列表页存在一项,数据直接在源码中可以看到,故直接抓取即可。

def get_list(cate):
    # 获取总页码,循环抓取所有页

    res = requests.get(
        f"http://ask.ci123.com/categories/show/{cate}", headers=headers)

    pattern = re.compile(
        r'<a class="list_last_page" href="/categories/show/\d+/all\?p=(\d+)"', re.S)
    totle = pattern.search(res.text).group(1)
    for page in range(1, int(totle)):
        print(f"http://ask.ci123.com/categories/show/{cate}/all?p={page}")
        time.sleep(0.2)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

从 1 开始循环到总页码

本部分代码比较容易,已经在上述代码实现。结果如图所示:

Python 爬虫小课 1-12 宝妈程序媛福利-育儿网问答数据抓取

本案例收尾环节

后续的内容就变得非常容易了,对每页数据进行分析,并进行存储数据操作,下述代码未编写存储部分,抓取部分代码已经填写完整,其中存在一个非常大的正则表达式,可以参考一下,如果爬取数据不是很严格,大量的使用 .*\s 这些常见元字符即可。

import requests
import re
import time

url = "http://ask.ci123.com/"
headers = {
    'user-agent': 'Baiduspider-image+(+http://www.baidu.com/search/spider.htm)'
}


def get_detail(text):
    # 该函数实现解析页面数据,之后存储数据
    pattern = re.compile(r'<li>[.\s]*<a href="/questions/show/(\d+)/" title="(.*?)" class="list_title" target="_blank" >.*?</a>\s*<span class="list_asw">(\d+)<font>.*?</font></span>\s*<a class="list_author"  href="/users/show/\d+" title=".*?">(.*?)</a>\s*<span class="list_time">(.*?)</span>\s*</li>')
    data = pattern.findall(text)
    print(data)
    # 数据存储代码不在编写


def get_list(cate):
    # 获取总页码,循环抓取所有页

    res = requests.get(
        f"http://ask.ci123.com/categories/show/{cate}", headers=headers)

    pattern = re.compile(
        r'<a class="list_last_page" href="/categories/show/\d+/all\?p=(\d+)"', re.S)
    totle = pattern.search(res.text).group(1)
    for page in range(1, int(totle)):
        print(f"http://ask.ci123.com/categories/show/{cate}/all?p={page}")
        res = requests.get(
            f"http://ask.ci123.com/categories/show/{cate}/all?p={page}", headers=headers)

        time.sleep(0.2)
        # 调取列表页数据提取函数
        get_detail(


    
res.text)


# 抓取分类页面
def get_category():
    res = requests.get("http://ask.ci123.com/", headers=headers)
    pattern = re.compile(
        r'<li><a href="/categories/show/(\d+)">', re.S)
    categories_ids = pattern.findall(res.text)
    print(f"获取到的分类ID如下:", categories_ids)
    for cate in categories_ids:
        get_list(cate)
        time.sleep(1)


if __name__ == "__main__":
    get_category()

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52

广宣时间

如果你想跟博主建立亲密关系,可以关注博主,或者关注博主公众号“ 非本科程序员 ”,了解一个非本科程序员是如何成长的。
博主 ID: 梦想橡皮擦 ,希望大家点赞、评论、收藏

Python 爬虫小课 1-12 宝妈程序媛福利-育儿网问答数据抓取
爬虫百例教程导航链接 : https://blog.csdn.net/hihell/article/details/86106916

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/74832
 
1033 次点击