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

「这是我参与11月更文挑战的第10天,活动详情查看:2021最后一次更文挑战

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

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

爬虫前的分析

类别页面分析

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

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

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

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

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

1
2
3
4
txt复制代码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}

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

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

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

问题列表页面分析

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

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

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

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

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

1
txt复制代码http://ask.ci123.com/categories/show/4/all?p={页码}

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

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

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

逻辑编码(伪代码)

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

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

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

爬虫正式编码

request 库 get 方法说明

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

1
2
3
4
5
6
7
8
9
10
11
12
python复制代码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()

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

必选参数 url

1
python复制代码requests.get(url="http://ask.ci123.com/")

传递 URL 参数

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

1
2
3
4
python复制代码import requests
payload = {'key1': 'value1', 'key2': 'value2'}
res = requests.get(url="http://ask.ci123.com/", params=payload)
print(res.url)

key1 为键名,value1 为键值。

定制请求头

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

1
2
3
4
5
6
7
8
python复制代码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)

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

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

1
2
3
4
5
6
7
8
9
python复制代码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
python复制代码r = requests.get('http://github.com', allow_redirects=False)

超时

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

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

常规代码如下:

1
2
3
4
5
6
7
8
9
python复制代码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)

高级部分参数

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

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

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

获取所有的分类页面地址

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
python复制代码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()

循环所有的分类页面地址

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

1
2
3
4
5
6
7
8
9
10
11
python复制代码# 抓取分类页面
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)

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
python复制代码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 开始循环到总页码

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

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

本案例收尾环节

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

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
python复制代码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()

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%