Scrapy爬取“动态”超链接网页

0 准备工作

本例涉及工具:

Python 3.7.7
Python Lib:
      Scrapy 2.4.1
      scrapy-splash 0.7.2
Docker Desktop 3.10
Docker Image:
      scrapinghub/splash 3.5
Burp Suite Professional 2020.12.1

1 创建并配置Scrapy项目

1.1 Splash配置

由于Scrapy自身无法执行JS脚本,因此通过Splash代理服务执行JS脚本
先打开Docker Desktop并执行以下命令下载镜像

docker pull scrapinghub/splash

再执行以下命令启动容器

docker run -it -p 8050:8050 --rm scrapinghub/splash

详情可参阅Splash Installation

1.2 创建项目

scrapy startproject jobDigger

详情可参阅Scrapy Tutorial

1.3 配置Scrapy

打开Scrapy项目目录下的settings.py修改或增加以下内容

ROBOTSTXT_OBEY = False //防止Scrapy遵守robot.txt协议
SPLASH_URL = 'http://localhost:8050' //Spalsh服务地址
DOWNLOADER_MIDDLEWARES = {
    'scrapy_splash.SplashCookiesMiddleware': 723,
    'scrapy_splash.SplashMiddleware': 725,
    'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 810,
}
SPIDER_MIDDLEWARES = {
    'scrapy_splash.SplashDeduplicateArgsMiddleware': 100,
}
DUPEFILTER_CLASS = 'scrapy_splash.SplashAwareDupeFilter'
HTTPCACHE_STORAGE = 'scrapy_splash.SplashAwareFSCacheStorage'

详情可参阅scrapy-splash说明

2 网页分析

步骤一

在浏览器中浏览http://www.career.zju.edu.cn发现网站内点击链接后浏览器地址栏没有发生变化,网页也没有刷新,因此基本可以判断是JS发送了异步请求(GET和POST请求都有可能),有兴趣的可以花时间从网页里面扒JS代码,看看是怎么处理的,这里没有必要,我们可以直接用Burp Suite获取需要的HTTP请求(Charles,Surge等抓包工具也可以抓取,Burp Suite可以拦截请求,一步步查看请求,这样可以更加快速地找到需要的请求)。

步骤二

打开Burp Suite,进入Proxy选项卡,使用内置浏览器打开网址,先通过所有请求直至网页显示完全,然后点击网页导航栏的“招聘咨询-招聘信息-综合招聘信息”,然后在紧接着的请求中可以筛选出获取招聘信息列表的链接,如下图所示。
招聘信息链接请求头

接着进入HTTP history选项卡查看该请求的请求体,通过观察可以发现请求体为HTML,其中链接形式有两种,一种是通过绑定“onclick”事件动态生成链接,链接生成函数为“add(this)”,“href”属性为“javascript:void(0)”;另一种是直接通过href属性定义的超链接。如下图所示。
动态生成链接
href链接

拉到网页源码底部可以看到脚本代码,其中有用的函数有两条——function goPage(分页),function add(动态生成链接)。
观察goPage函数可以分析出获取招聘信息的网页链接,从而对上面获取的链接进行精简(实际上链接中lmjdid和&_字段可以省去),其中minCount和maxCount两个参数决定了获取信息的数量。
观察add函数可以发现,链接主要是根据a标签的三个自定义属性data-src、data-xwid、data-lmtype生成的,链接拼接的表达式为“url=_path+lmdz+'spid='+spid+'&lmtype='+lmtype”,其中只有一个变量“_path”不确定,这时可以在浏览器中按F12进入开发者模式,选择Console选项卡,输入console.log(_path)即可获取其值为“/jyxt”。
获取_path变量值

此时,所有准备工作都齐全了。

3 爬虫代码

根据以上的分析,编写出代码也没有难度了,在spider文件夹下新建一个文件zju.py,参考代码如下所示。

import scrapy
from scrapy_splash import SplashRequest

minCount = 0
maxCount = 30
start_urls = f'http://www.career.zju.edu.cn/jyxt/jygz/new/getContent.zf?minCount={minCount}&maxCount={maxCount}&sjlmid=A188D7448980110BE055134CA39C9BD7&lmtype=2&lx=3'

class mainDigger(scrapy.Spider):
    name = "zju"

    # 通过Spalsh爬取起始网页
    def start_requests(self):
            yield SplashRequest(start_urls, callback=self.parse, args={'wait': 5})

    def parse(self, response):
        # 保存爬取的网页
        src = response.url.split('/')[-1]
        filename = f'job-{self.name}-{src}.html'
        with open(filename, 'wb') as f:
            f.write(response.body)
        # 提取就业信息列表
        ulData = response.css("li.news-ctn")
        print(ulData.getall())
        basePath = '/jyxt'

        for li in ulData:
            # 提取标签属性并生成链接,提交下一次爬取地址
            if li.css("a::attr(href)").get() == r"javascript:void(0)":
                attrSrc = li.css("a::attr(data-src)").get()
                attrXwid = li.css("a::attr(data-xwid)").get()
                attrLmtype = li.css("a::attr(data-lmtype)").get()
                nextPath = basePath + attrSrc + 'xwid=' + attrXwid + '&lmtype=' + attrLmtype
                print(nextPath)
                yield response.follow(nextPath, callback=self.parse)
            else:
                nextPath = li.css("a::attr(href)").get()
                print(nextPath)
                yield response.follow(nextPath, callback=self.parse)
posted @ 2021-01-23 14:08  Mr_Purity  阅读(213)  评论(0)    收藏  举报