1.认识CrawlSpider
通用爬虫框架是旨在爬取一些类似的spiders时减少代码的重复编写,用一个模板来实现爬取不同的spiders,把这些不同的部分做成配置文件,然后再爬取的时候动态配置到这个模板中,于是就实现了只需编写一个spider就可以爬取多个网页。
在写这个模板的时候,要编写这个spider继承自CrawlSpider,他是Spiders类的子类。
1.它新增了一个新的属性:
rules
它是一个Rule对象列表,借助Rule对象,可以按照Rule对象里定义的规则从页面中提取Url链接,并自动发送Request请求。如果有多个Rule时,只会执行第一个Rule,当符合第一个Rulede的链接被提取完了的时候才会去提取下一个链接。比如要从多个页面中提取链接,而每一个页面中都有多个链接,提取待提取链接的Rule放在第一个,提取下一页链接的Rule放在其后,这样就会从多个页面中提取需要提取的所有链接。
2.parse_item(response)
解析Rule提交的请求获得的页面不再用parse,而是用parse_item.
具体的用法在后面的例子中。
3.parse_start_urls(response)
它取消了解析start_url函数
认识Rule
class scrapy.spiders.Rule(link_extractor, callback=None, cb_kwargs=None, follow=None, process_links=None, process_request=None)
#link_extractor:
#内部定义了提取链接的规则
classscrapy.linkextractors.LinkExtractor(allow=(), deny=(), allow_domains=(), deny_domains=(), deny_extensions=None, restrict_xpaths=(), restrict_css=(), tags=('a', 'area'), attrs=('href', ), canonicalize=False, unique=True, process_value=None, strip=True)
1.关于LinkExtractor的用法详情见https://docs.scrapy.org/en/latest/topics/link-extractors.html#topics-link-extractors 常用的是restrict_xpath()(通过xpath来定位要提取链接的位置)和allow(),可以几个结合着用。
2.callback参数是一个字符串。他是解析Rule提取出来的链接获得的页面的函数名,一般为 parse_item.
2.cb_kwargs
字典,传递给回调函数即callback的参数
3.
process_links
一个字符串,一个回调函数的函数名,定义在该spider类中的实例方法。主要用来过滤获得的链接。
4
process_request
同样是一个字符串,一个实例方法的函数名,用来对Rule获得的link 产生的Request对象进行一些修改和加工。
终于说完了这个类。
下面言归正传,怎么用这个通用框架。
首先先建一个项目,然后在项目目录下输入
scrapy genspider -t crawl universal universial
//名字随意
用pycharm打开项目
1。在根目录下建立一个rules.py,用来写提取页面的rules
格式如下例所示
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import Rule
rules = {
'china': (
Rule(LinkExtractor(allow='article/.*\.html',restrict_xpaths='//div[@id="left_side"]//div[@class="con_item"]'),
callback='parse_item'),
Rule(LinkExtractor(restrict_xpaths='//div[@id="pageStyle"]//a[contains(.,"下一页)"]'))
)
}
2.强某一个spider的解析方法,以及一些属性和配置写成一个json文件,在根目录下建立一个configs文件夹,放在其中。键名字可以根据自己的习惯来取。文件名取spider得名字。
格式如下例所示
{
"spider": "universal",
"website": "中华科技网",
"type": "新闻",
"index": "http://tech.china.com/",
"settings": {
"USER-AGENT": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"
},
"start_urls": [
"http://tech.china.com/articles/"
],
"allowed_domains": ["tech.china.com"],
"rules": "china",
"item": {
"class": "NewsItem",
"loader": "ChinaLoader",
"attrs":{
"title": [{"method": "xpath","args":["//h1[@id=\"chan_newTitle\"]/text()"]}],
"url": [{"method": "attr", "args": ["url"]}],
"text": [{"method": "xpath","args": ["//div[@id=\"chan_newsDetail\"]//text()"]}],
"datatime": [{"method": "xpath","args": ["//div[@id=\"chan_newsInfo\"]/text()"], "re":"(\\d+-\\d+-\\d+\\s\\d:\\d:\\d)" }],
"website": [{"method": "value","args": ["中华科技网"]}],
"source": [{"method": "xpath","args": ["//div[@id=\"chan_newsInfo\"]/text()"],"re": "来源:(.*)"}]
}
}
}
把每一个spider的特有配置,数据提取方式写成一个json文件,在运行不同的spider的时候再把这些属性和方法动态配置到universal中去,接下来就是要实现动态配置的方法。
3.现定义一个函数,位置在项目中任意位置都行。
如下
from os.path import realpath,dirname
import json
def get_configs(name):
path = dirname(realpath(__file__)) + '/configs/' + name + '.json'
with open(path, 'r', encoding='utf-8') as f:
return json.loads(f.read())
这个函数将在universal.py中的CrawlSpider初始化中被调用。
4.接下来初始化CrawlerSpider 和定义CrawlSpderz中的其他方法。如下例
from crawlTestProj.utils import get_configs
from scrapy.spiders import CrawlSpider,Rule
from crawlTestProj.configs.rules import rules
from crawlTestProj.items import NewsItem
from crawlTestProj.items import ChinaLoader
class UniversalSpider(CrawlSpider):
name = 'universal'
def __init__(self, name, *args, **kwargs):
config = get_configs(name)
self.config = config
self.rules = rules.get(config.get('rules'))
self.start_urls = config.get('start_urls')
self.allowed_domains = config.get('allowed_domains')
super(UniversalSpider, self).__init__(*args, **kwargs)
def parse_item(self,response):
item = self.config.get("item")
if item:
cls = eval(item.get('class'))()
loader = eval(item.get('loader'))(item=cls, response=response)
for key, value in item.get("attrs").items():
for extractor in value:
if extractor.get('method') == 'xpath':
loader.add_xpath(key, *extractor.get("args"), **{'re': extractor.get("re")})
if extractor.get("method") == "css":
loader.add_css(key, *extractor.get("args"), **{"re": extractor.get('re')})
if extractor.get("method") == "attr":
loader.add_value(key, getattr(response, *extractor.get("args")))
if extractor.get("method") == "value":
loader.add_value(key, *extractor.get("args"))
yield loader.load_item()
5.定义run函数
为了方便在根目录下
import sys
from scrapy.utils.project import get_project_settings
from crawlTestProj.spiders.universal import UniversalSpider
from crawlTestProj.utils import get_configs
from scrapy.crawler import CrawlerProcess
def run():
name = sys.argv[1]
custom_settings = get_configs(name)
#爬取使用的spider名称
spider = custom_settings.get('spider', 'universal')
project_settings = get_project_settings()
settings = dict(project_settings.copy())
settings.update(custom_settings.get('settings'))
process = CrawlerProcess(settings)
#启动爬虫
process.crawl(spider, **{'name': name})
process.start()
if __name__ == "__main__":
run()