我们在做分布式爬虫的时候有些网站可能因为规则变更使得我们爬虫也要跟着变更规则,如果部署了很多的话更新规则就会是个头大的问题,慢慢去一个一个更新爬虫规则实在是太累了,所以我就想到了用xml来做规则,让爬虫去读取一个远程xml就可以了;我们后期维护的时候也只用维护这个xml文件即可。
我用的是scrapy-redis做的分布式
概述:
1、主要是通过request读取远程xml
2、通过ElementTree来解析xml
3、通过字典方式读取具体规则
举个栗子:
服务器端的xml规则文件:
<?xml version='1.0' encoding='utf-8'?>
<XpathInfo shelf="XpathInfo">
<movie title="CCCCC">
<company_buy>//div[@class='showroom-nav']//li[@class='showroom-nav-item ']/a/@href</company_buy>
<com_buy_list>//tbody//div[@class='purchase-title']/a/@href</com_buy_list>
<com_date>//tbody//div[@class='purchase-hd']/p/span[2]/text()</com_date>
<url_list>//div[@class='purchase-hd']/a/@href</url_list>
<dates>//table[@class='tb-purchase']/tbody/tr/td//div[@class='purchase-info']/text()</dates>
<tetle>//title/text()</tetle>
<tags>//meta[@name='Keywords']/@content</tags>
<content>//meta[@name='Description']/@content</content>
<htmltext>//body</htmltext>
<img_url>//ul[@class='thumb-lst']//a/@href</img_url>
<company>//div[@class='side-corp-hd']/h3/a/text()</company>
<fabu_date> </fabu_date>
</movie>
<movie title="aaaa">
<tetle>我是tetle2</tetle>
<tags>我是tags2</tags>
<content>我是content2</content>
<htmltext>我是htmltext2</htmltext>
<img_url>我是img_url2</img_url>
<company>我是company2</company>
<fabu_date>我是fabu_date2</fabu_date>
</movie>
</XpathInfo>
注意:
我这个做的是三层节点
根节点:XpathInfo (可自定义)
子节点: movie 属性值:CCCCC和aaaa(这里就可以根据不同的爬虫名称来定义自己的属性值了)
龟孙子节点:<xxxx>..</xxxx>(这里主要是具体到每一个规则的标签,名称根据要提取的数据来自定义)
Scrapy爬虫端:
新建一个 rule.py文件来远程获取这些规则(返回dict类型)
# -*- coding: utf-8 -*-
import xml.etree.ElementTree as ET
from urllib import request
from settings import XML_URL
class RuleInfo():
"""获取远程Xpath或css规则"""
def __init__(self):
self.url = XML_URL # 配置URL
res = request.urlopen(self.url) # 读取网页信息
tree = ET.parse(res) # 传递给ET进行解析
self.root = tree.getroot() # 生成节点
def obtain(self, name): # 传递需要获取的title
for child in self.root:
if child.attrib["title"] == name:
ruledict = {}
for children in child:
ruledict[children.tag] = children.text
return ruledict
if __name__ in "__main__":
print(RuleInfo().obtain("aaaa"))
注:
我导入的 XML_URL 是在settings文件中自定义的一个变量,该变量用于储存xml的url(填写自己xml的url)
ElementTree是python的标准库,无需安装
我们现在就可以测试是否返回我们需要的规则了(dict类型)
打印信息:
{'tetle': '我是tetle2', 'tags': '我是tags2', 'content': '我是content2', 'htmltext': '我是htmltext2', 'img_url': '我是img_url2', 'company': '我是company2', 'fabu_date': '我是fabu_date2'}
如果我填写CCCCC则打印:
{'company_buy': "//div[@class='showroom-nav']//li[@class='showroom-nav-item ']/a/@href", 'com_buy_list': "//tbody//div[@class='purchase-title']/a/@href", 'com_date': "//tbody//div[@class='purchase-hd']/p/span[2]/text()", 'url_list': "//div[@class='purchase-hd']/a/@href", 'dates': "//table[@class='tb-purchase']/tbody/tr/td//div[@class='purchase-info']/text()", 'tetle': '//title/text()', 'tags': "//meta[@name='Keywords']/@content", 'content': "//meta[@name='Description']/@content", 'htmltext': '//body', 'img_url': "//ul[@class='thumb-lst']//a/@href", 'company': "//div[@class='side-corp-hd']/h3/a/text()", 'fabu_date': ' '}
集成到爬虫当中:
from scrapy.spiders import Rule
from scrapy.linkextractors import LinkExtractor
from scrapy_redis.spiders import RedisCrawlSpider
from rule import RuleInfo # 【重点】导入我们刚才写的RuleInfo类
class MyCrawler(RedisCrawlSpider):
"""Spider that reads urls from redis queue (myspider:start_urls)."""
name = 'mycrawler_redis'
redis_key = 'mycrawler:start_urls'
ru = RuleInfo().obtain(name) # 【重点】新增一个类变量,并且直接获取规则返回的字典
rules = (
# follow all links
Rule(LinkExtractor(), callback='parse_page', follow=True),
)
def parse_page(self, response):
# 【重点】下面直接获取类变量的dict就可以了。
abcd = response.xpath(self.ru["company_buy"]).extract()
efig = response.xpath(self.ru["com_date"]).extract()
......省略.......
重点1:导入我们写的RuleInfo类
重点2:新建类变量,自动获取节点名称下面的规则
重点3:提取规则时直接访问类变量的dict对应的规则
希望大家可以交流一下!谢谢!