爬虫 | 白菜价商品数据抓取

发布时间 2023-07-29 09:47:44作者: 张Zong在修行

本实验介绍了一个全新的爬虫思路,通过移动端 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 XUA 参数如下图红框区域所示。

将如下代码写入 /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&timesort=1607519513

这里再补充一个小技巧,通过开发者工具可以过滤特定内容的请求,例如当我们发现什么值得买的白菜商品数据返回的接口都相似时,并且都包含 ajax_search_list 这个字符串,可以在下图所示区域进行特定字符串过滤。

当上图文本框输入特定字符之后,下面的请求只有包含这个特定字符串的链接才会显示出来,这样可以只显示对我们有用的请求。

本案例在编写时还有一个点需要学习一下,在爬虫程序编写过程中经常遇到,就是第二次的爬取请求中包含第一次请求返回的数据。

首先罗列一下已经筛选到的两个请求地址。

https://m.smzdm.com/search/ajax_search_list?type=tag&channel=faxian&search_key=白菜党&timesort=1607519513
https://m.smzdm.com/search/ajax_search_list?type=tag&channel=faxian&search_key=白菜党&timesort=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 站点,为你提供两条编写爬虫的新方向,希望学习之后,你爬虫程序的编写思路不在单一。