爬取流程分析
京东有点好就是有个领券中心,所有购物券都集中在一个页面,可以通过scrapy等爬虫框架很容易的爬取。其中,一个购物券部分的代码如下:
这段代码中 class = price 和 range 的部分包含了购物券的信息,而 data-linkurl 属性的值则是该优惠券适用的商品。这种结构非常适合于用xpath或beautifulsoup等进行语义分析,只需要爬取领券中心的100页的信息,并且跟踪data-linkurl就可以获取有优惠券的商品。说干就干,把初学python时用过的scrapy翻出来练练手。
Scrapy简介
scrapy是一个爬虫框架,在今年年初更新到了python3的版本,因此就用py3开撸,顺便看看scrapy对py3的支持怎么样。对于框架这种东西,在初学时是给我造成了不小的困扰,和使用模块时一点一点增量式的搭建不一样,scrapy这种框架更多的工作是配置工作,因为很多东西是scrapy在框架里提供了,需要按照scrapy的工作流去补充需要的功能。空讲无益,先看看scrapy的架构的流程图。
架构概览的详细资料可以看官方文档,简单的说就是Spiders将Requests注册到Scheduler,Scheduler控制下载队列去下载网页,返回一个Response给Spiders,Spiders处理之后将items交给后端。
具体到代码中就是继承一个scrapy.Spider类,该类默认parse函数为核心的处理接口。从默认的starturl下载的response在这个函数里解析,用xpath等解析出来的items可以在这里返回,返回到pipeline里自定义的数据库接口中去。如果需要跟踪二级链接,则需要返回一个Requests重新将二级链接注册到下载队列,并设置二级链接的回调函数。在初学scrapy和这次编写JD的爬虫时都遇到没有爬取二级链接的情况,鼓捣了半天发现原因是没有返回Requests或没有在parse中返回。
Spider类默认是将response给到parse函数,也可以在start_requests函数中返回Requests来自定义回调函数。无论是默认的parse函数还是自定义的回调函数,如果返回items,dict或Requests,都会有spider的后处理动作,无返回值不会,所以一旦spider抓取有问题,就看看parse函数的返回值是不是都正常返回了,而且是要在parse这一级的函数里返回,子函数返回不算。
总而言之,这个框架的数据流中的中心节点接口是parse函数,比较有用的方法是Requests方法,多注意parse函数的返回值有木有问题。
代码
源代码已经上传到github,主要代码在coupons\spiders\coupons_spider.py中:
import scrapy
from coupons.items import CouponsItem
import json
class CouponsSpider(scrapy.Spider):
name = "coupons"
allowed_domains = ['jd.com']
start_urls = ["http://a.jd.com/coupons.html?page={}"\
.format(x) for x in range(1, 100)]
def parse(self, response):
pages = response.xpath("//*[@class=\"quan-item quan-d-item quan-item-acoupon\"]")
for page in pages:
metadata = self._xpathCounponsMetaData(page)
url = 'http://'+ metadata['dataLinkurl'][0]
yield scrapy.Request(url=url, meta=metadata, callback= self.classify)
def _xpathCounponsMetaData(self,page):
"""xpath rules"""
...
def classify(self, response):
"""second url callback"""
...
def _parseGoods(self, response):
"""second url xpath rules"""
...
def close(spider, reason):
"""scrapy's work is down, proccessing data"""
...
基本流程就是在start_urls里设置好爬取的一级网址列表,默认的parse()爬取,_xpathCounponsMetaData()解析html,在parse中设置二级链接的回调函数为classify(),classify()中的_parseGoods解析html,classify()返回一个items供pipelines后处理。最后在close中处理爬取完所有链接后的数据。注意别忘了在items.py中设置items,在settings.py中注册好pipelines,在pipelines里我就简单的搞了哥json保存,没有用数据库,在后面可以改进。
写在最后
写完了发现这个东西并没那么好用,虽然可以查询哪些商品有优惠券,却不能通过优惠券信息对应到该优惠券,只能手动筛选,还是挺鸡肋的,权当练手了。
优惠券中心虽然有一百页,但二级链接中基本只有seach开头的链接是比较有价值的,因此,并不用爬取一百页。而且二级链接中也是分页的,我只爬取了第一页,应该漏了很多信息。