爬虫 | 白菜价商品数据抓取
本实验介绍了一个全新的爬虫思路,通过移动端 Web 站点爬取数据,方法是借助谷歌浏览器的开发者工具,模拟出移动设备进行网站访问,然后去获取移动端网站的数据接口。后半部分通过爬取 4399 排行榜与什么值得买白菜商品两个案例,强化对于移动端 Web 站点爬取技术的学习。
知识点
- 移动端 Web 站点简介
- 4399 手机站排行榜抓取
- 白菜价商品抓取
移动 Web 站点简介
上一实验为大家讲解的内容是基于接口爬取数据,属于爬虫分析类知识,本实验将为你延伸出第二种常见爬虫技巧,爬取手机 Web 站点。
很多网站在设计的时候,都会专门开发一版移动端 Web 站点,也就是通过手机浏览器访问的站点。很多公司对于移动端站点的反爬措施并不像 PC 站点一样严格,基于这样的“疏漏“,我们可以尝试从移动端 Web 网站入手去抓取数据。
通过开发者工具访问移动端 Web 站点
在谷歌浏览器的开发者工具中,可以便捷的访问移动站点,以下案例选用 58 同城(由于58同城网站会自动进行定位,所以下图会展示你所在城市的相关内容),具体操作如下,在浏览器打开 58 官网,默认显示如下图所示:
唤醒浏览器开发者工具,点击下图位置手机与平板图标。
点击上图小图标之后,还要刷新一下页面,就可以看到 58 同城网站在移动端预览效果:
可以通过顶部的控制工具,切换模拟手机的型号以及其他设置。具体每个按钮对应的效果,自行点击查看对应的变化即可。例如本案例为 iPhone X
手机下显示的效果(该效果为浏览器模拟展示,部分情况下和真机存在差异,对于爬虫学习影响不大)。
一般在模拟移动端浏览器操作时,习惯性地将开发者工具置于浏览器右侧,点击下述区域 1,2 处按钮进行配置。
移动端网站因为涉及网络流量的问题,部分界面的结构会变得简单清晰,例如 58 同城二手房频道,在移动端界面只呈现重要数据。
清晰的页面结构能大幅度降低爬虫程序编写的难度。
这里提示一个小技巧,如果发现一个网站,当切换到移动端访问的时候,域名发生了变化,例如上述案例中 58 同城网站由 https://www.58.com/
变为了 https://m.58.com/
,此时大概率网站的接口也是单独为移动端研发的,只要接口是单独研发,很容易出现逻辑上的疏漏,导致移动端站点的反爬度降低,所以当你找到一个网站存在移动站点,建议针对该移动站点编写爬虫。
4399 游戏排行榜案例实操
打开 4399 游戏页面,通过开发者工具切换到移动端访问,点击排行按钮,下拉页面,在 Network
中寻找数据返回接口。当数据接口获取到之后,后续的编码与普通接口的调用一致,只需要注意一项内容即可,后文会有所涉及。
开发者工具切换到 Network
选项卡,在各请求中寻找数据接口,一般情况下勾选 XHR
即可筛选出数据类的接口,注意是一般情况下,存在特殊情况。在本案例中,切换之后,可以快速捕获到接口。
最终获取到接口请求地址如下,反复获取寻找相应的规律。
http://gprp.4399.com/cg/online/get_h5_ranking.php?cId=2&devicesupport=1&page_size=20&page_num=1&dataTraceId=880
http://gprp.4399.com/cg/online/get_h5_ranking.php?cId=2&devicesupport=1&page_size=20&page_num=2&dataTraceId=880
......
随着滚动下拉,发现受影响的参数为 page_num
,该参数为页码,所以通过不断增加该页码即可获取到排行榜所有数据。通过反复测试,发现当页码为 9 时,数据返回为空,故排行榜数据为 8 页。
上文提及的在爬取移动端接口的时候,需要注意一项内容,该内容为请求头中的 User-Agent
参数,前面的实验中已经对该参数进行了相关说明。该参数的内容会告知服务器客户端访问设备信息,所以在访问移动端网站的时候,建议使用特定的手机端浏览器 UA
,该内容可直接在开发者工具中获取。例如本 iPhone X
的 UA
参数如下图红框区域所示。
将如下代码写入 /home/project/4399.py
文件:
import requests
def get_data(page_num=1, page_size=20):
headers = {
"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1"
}
# URL 参数,其中部分为固定值
data = {
"cId": 2,
"devicesupport": 1,
"page_size": page_size,
"page_num": page_num,
"dataTraceId": 880
}
url = "http://gprp.4399.com/cg/online/get_h5_ranking.php"
res = requests.get(url,
params=data, headers=headers)
# 直接以 JSON 格式返回
print(res.json())
if __name__ == "__main__":
# 直接调用
get_data()
# 循环调用
for i in range(1, 9):
print("正在抓取第{i}页数据".format(i=i))
get_data(page_num=i)
对比前面实验中的网页接口抓取,移动端接口数据抓取,只需要修改 UA
参数,让其返回移动数据即可,其余代码逻辑基本一致,本实验重点关注如何寻找移动端数据接口相关知识点。
白菜价商品数据案例实操
本实验中第二个案例为 什么值得买白菜商品 移动站抓取,首先使用开发者工具进行接口定位,切换到 XHR
选项卡,抓取到数据地址如下。
https://m.smzdm.com/search/ajax_search_list?type=tag&channel=faxian&search_key=%E7%99%BD%E8%8F%9C%E5%85%9A×ort=1607519513
这里再补充一个小技巧,通过开发者工具可以过滤特定内容的请求,例如当我们发现什么值得买的白菜商品数据返回的接口都相似时,并且都包含 ajax_search_list
这个字符串,可以在下图所示区域进行特定字符串过滤。
当上图文本框输入特定字符之后,下面的请求只有包含这个特定字符串的链接才会显示出来,这样可以只显示对我们有用的请求。
本案例在编写时还有一个点需要学习一下,在爬虫程序编写过程中经常遇到,就是第二次的爬取请求中包含第一次请求返回的数据。
首先罗列一下已经筛选到的两个请求地址。
https://m.smzdm.com/search/ajax_search_list?type=tag&channel=faxian&search_key=白菜党×ort=1607519513
https://m.smzdm.com/search/ajax_search_list?type=tag&channel=faxian&search_key=白菜党×ort=1606919421
上述请求地址中,只有最后一个参数 timesort
发生了变化,而且两次请求的地址中没有明显的分页数据,此次就要特别注意,第二次(第 N 次)请求是不是基于第一次(第 N-1 次)请求演化而来。
再次核对本案例第一次请求返回的数据,在接口返回数据的最后一条中发现 timesort
参数,该参数的值为 1606919421
,恰好与第二次请求中的 timeout
值相对应,也证实了我们之前的猜测。
接下来的代码将采用伪代码进行编写,书写核心逻辑部分。
# 声明抓取函数,timeout 参数初始值需要手动获取,即第一次获取到的初始链接中的值,本案例中对应上文的 1607519513
def get_items(timeout):
# 通过 requests 模块获取网页数据
# 对获取到的 JSON 格式数据进行判断,如果包含数据,存入本地文件,否则直接结束
# 获取 JSON 数据中最后一条数据的关键参数,本案例中为 timeout
# 重新调用 get_items 方法,传入新的 timeout 值
上文提及的代码逻辑,可普遍用于类似本案例形式,即新的数据是基于上次请求返回的数据进行再次获取的。
在实际编码过程中,你可以先通过上述方式将自己的逻辑整理清晰,在将汉语逻辑翻译成代码。
将如下代码写入 /home/project/smzdm.py
文件:
import requests
import time
# 声明抓取函数,timeout 参数初始值需要手动获取,即第一次获取到的初始链接中的值,本案例中对应上文的
def get_items(timeout=1607519513):
headers = {
"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1"
}
url = "https://m.smzdm.com/search/ajax_search_list"
data = {
"type": "tag",
"channel": "faxian",
"search_key": "白菜党",
"timesort": timeout
}
# 通过 requests 模块获取网页数据
res = requests.get(url,
params=data, headers=headers)
# 对获取到的 JSON 格式数据进行判断,如果包含数据,存入本地文件,否则直接结束
json_data = res.json()
# 获取所有商品
goods = json_data["data"]
if len(goods) > 0:
print("商品总数为:", len(goods))
# 该部分可以调用存储函数,存储成 CSV 文件
print(goods)
# 获取 JSON 数据中最后一条数据的关键参数,本案例中为 timeout
last_data = goods[-1]
# print(last_data)
timeout = last_data["time_sort"]
# 重新调用 get_items 方法,传入新的 timeout 值
print(timeout)
time.sleep(2)
get_items(timeout)
if __name__ == "__main__":
get_items()
通过伪代码进行逻辑梳理,再将伪代码翻译成真代码,是编程学习阶段一个非常实用的技巧,它可以有效解决面对编程目标,不知从何处开始编写的难题。
上述代码运行结果与数据存储,由大家自行补充完整。
实验总结
本实验依旧从思路上,扩展你编写爬虫程序的知识面。爬取目标网站的移动端 Web 站点,很多时候可以出现意想不到的效果,移动端 Web 站点是介于电脑网站与手机 APP 中间的一种载体,它更像是一种过渡项目,很多公司在移动端 Web 站点技术投入较少,所以为爬虫程序提供了更大的可能性。
本实验与上一实验,分别从解析网页到解析接口,从解析 PC 站点到解析移动端 Web 站点,为你提供两条编写爬虫的新方向,希望学习之后,你爬虫程序的编写思路不在单一。