Python爬虫基础②


前言

本篇的内容接上一篇,主要讲的是AJAX,JSON解析和正则表达式。

我这里不重复注意事项,如果你有不明确的请去看一下上一篇爬虫基础。

AJAX交互:在使用网页中,有时候需要获取的信息放在对面服务器里并不会直接给你,而是需要你通过查询的方式才会返回相应的数据,返回的不是一整个网页,而是你所需要的数据,这样子可以减少服务器的带宽占用。

JSON是一种轻量级资料交换格式,其内容由属性和值所组成,因此也有易于阅读和处理的优势,所以大多数服务器的数据发送接收都采用JSON格式。

正则表达式作为用简单字符串来描述、匹配文中全部匹配指定格式的字符串,现在很多文本编辑器都支持用正则表达式搜索、取代匹配指定格式的字符串,检索效率能快不少。

AJAX

传统的Web允许用户端填写表单(form),当提交表单时就向网页服务器发送一个请求。服务器接收并处理传来的表单,然后送回一个新的网页,但这个做法浪费了许多带宽,因为在前后两个页面中的大部分HTML码往往是相同的。由于每次应用的沟通都需要向服务器发送请求,应用的回应时间依赖于服务器的回应时间。这导致了用户界面的回应比本机应用慢得多。

与此不同,AJAX应用可以仅向服务器发送并取回必须的数据,而不是整个网页,并在客户端采用JavaScript处理来自服务器的回应。因为在服务器和浏览器之间交换的数据大量减少,服务器回应更快了。同时,很多的处理工作可以在发出请求的客户端机器上完成,因此Web服务器的负荷也减少了。

tips

我们这里以肯德基餐厅信息查询网站作为示例,演示AJAX的特征和交互方式,以及数据获取方式。

其他很多网站都是大同小异,你还可以去一些国家政务网站资料查询那里尝试,不过一次请求就够了别短时间爬取太多次,记住之前说的注意事项就行。

怎么识别是不是AJAX呢

当你进行一次请求或者刷新操作时,网页只有一部分内容发生了变化,网址没有变化。

类似于这样

或者说网址变化了,但是你用爬虫请求该网页时,发现本该出现的数据没有出现。

例如

我们先打开肯德基餐厅信息查询网站,然后打开浏览器控制台,然后在搜索框做一次请求,在网络选项卡下面就可以看到我们做的请求了,如果请求过多不好定位,可以在类别筛选里面选择AJAX或者叫Fetch/XHR来快速找到对应请求。

这里可以看到我们做出查询的关键词来定位我们的请求。

在响应里面可以找到服务器返回给我们的数据。

因为太多这里只展示一部分。

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"Table": [{
"rowcount": 192
}],
"Table1": [{
"rownum": 1,
"storeName": "开发区上海路",
"addressDetail": "开发区上海路80号利群时代超市一楼",
"pro": "24小时,Wi-Fi,点唱机,店内参观,礼品卡,手机点餐",
"provinceName": "江苏省",
"cityName": "南通市"
}]
}

接下来我们回到标头可以看到我们请求的网址,请求方法是POST还是GET,以及携带的表单,或者叫数据。这些数据向我们展示了如何规范地向服务区索取对应的数据,网址,格式,页码,参数等等。我们接下来的爬虫程序跟着做就行。

在前面的基础上面我们只需要对我们的请求加一点东西即可完成AJAX请求,可以看到,我们这次请求方法是POST,网址是

http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=keyword,数据如上。如果要请求相同到的东西,我们只需要写出下面的代码即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
import requests
kfc = 'http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=keyword'
header = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36'
}
data = {
cname: ,
pid: ,
keyword: 上海,
pageIndex: 11,
pageSize: 10
}
r = requests.post(url = kfc , headers = header , data = data)

这里的header上次讲过了,data的参数在POST里对应的就是你要向服务器提交的表单。URL对应的是上面POST请求的URL,不是你网址栏的URL

这样子,你就成功拿到了你想要的数据。

JSON解析

上面我们成功拿到了数据,但是是JSON字符串格式的,我们只需要里面某个类别的数据,比如所有餐馆的名字,那该怎么办呢?

现在就是让我们解析JSON格式数据输出的时候了。

想要使用官方自带的JSON库,我们照例还是得添加一行导入。

1
import json

然后我们得把获取到的JSON数据转化为字典。

我把下面JSON格式化只是让你们看清楚层级关系,你们拿到手的JSON文本都被压缩成了一整行,想要像这样子的话网络上面有很多格式化网站可供使用。

这样子很清楚能看到层级关系,方便数据解析操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
js = '''{
"Table": [{
"rowcount": 192
}],
"Table1": [{
"rownum": 1,
"storeName": "开发区上海路",
"addressDetail": "开发区上海路80号利群时代超市一楼",
"pro": "24小时,Wi-Fi,点唱机,店内参观,礼品卡,手机点餐",
"provinceName": "江苏省",
"cityName": "南通市"
}]
}'''

r = json.loads(js)

这样子r这个变量就变成了存储JSON的字典,按照PYTHON字典的方式控制即可

例如下面的代码就是把里面详细地址的值打印出来。

1
print(r["Table1"][0]["addressDetail"])

正则表达式

由于讲起来可是一门很深的学问,而且挺难的,所以这里就不细讲,主要也是因为自己水平也不过关,主要说最常用的匹配操作。

以后看各位需要再看要不要跟这次爬虫一样拆成几次讲完。

这个作为强大的处理工具,按理来说你使用熟练的话可以取代掉很多其他工具的数据解析工作。比如之前的lxml,JSON,bs等等。

在Python里,用re模块来导入相应的库。

接下来书写正则表达式

例子:(把下面的google链接提取出来)

可以这样写程序:

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

text = '''<script async="" defer=""
src="https://launchpad.privacymanager.io/1/launchpad.bundle.js"></script>

<script async="" src="https://www.googletagmanager.com/gtag/js?id=G-9JKZ3LJVCD"></script>

<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());

gtag('config', 'G-9JKZ3LJVCD');
</script>'''

ex = '<script async=.*?async="" src="(.*?)"></script>.*?</script>'
print(re.findall(ex , text , re.S)[0])
1
<script async=.*?async="" src="(.*?)"></script>.*?</script>

这句表达式里面,上面一共有三个.*?,他们连在一起代表匹配多个任意字符

如果你把普通字符写在正则里面代表匹配特定字符。上面表达式的效果,匹配字符串中在async="" src=""></script>中间的内容,也就是https://www.googletagmanager.com/gtag/js?id=G-9JKZ3LJVCD

带括号的代表你要查找的部分。re.S代表查找模式,一般数据解析只需要用这个模式就行。

这就是本篇所有内容了,感谢你的观看,如果有建议或者错误请指出,感谢。

召唤伊斯特瓦尔