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属性定义的超链接。如下图所示。


拉到网页源码底部可以看到脚本代码,其中有用的函数有两条——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”。

此时,所有准备工作都齐全了。
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)
浙公网安备 33010602011771号