| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
代号PE,为scrapy框架量身定制的 "可配置化的响应解析器引擎"。
主要支持以下特性:
安装最新体验版:
pip install git+https://github.com/Danceiny/parser_engine
安装稳定版:
pip install -U parser_engine
请移步CHANGELOG.md。
如何使用PE从零开始快速编写一个网站的爬虫,并持久化数据?可移步快速开始。
from parser_engine import TemplateAnnotation
from scrapy.spiders.crawl import CrawlSpider
@TemplateAnnotation(tpls="demo")
class DemoSpider4(CrawlSpider):
name = "demo4"
start_urls = [
"http://github.cannot.cc/baixing-helper"
]from parser_engine import TemplateAnnotation
from parser_engine.clue.spider import ClueSpider
@TemplateAnnotation(start_url_tpl=({
"name": "zhongguozhongqi_xiaoshouwangluo",
"itemname": "HuocheDealerItem",
"parent": {
"xpath": "//tr[@class=\"bgcolor2\"]"
},
"fields": [
{
"key": "area",
"xpath": "td[1]/text()",
"value_type": "stripped_string"
}, {
"key": "leads_name",
"xpath": "td[2]/text()",
"value_type": "stripped_string"
}, {
"key": "address",
"xpath": "td[3]/text()",
"value_type": "stripped_string"
}, {
"key": "phone",
"xpath": "td[5]/text()",
"value_type": "stripped_string"
}
]
}), channel='zhongguozhongqi', leads_src='中国重汽')
class ZhongguozhongqiSpider(ClueSpider):
name = 'zhongguozhongqi'
def parse(self, response):
items = self._parse_start_url(response)
for item in items:
phone = item.get('phone')
if phone:
item['phone'] = phone.replace('、', ',')
yield item
self.finish_clue(response, len(items))from parser_engine.clue.spider import ClueSpider
from parser_engine import TemplateAnnotation
from parser_engine.clue.items import ClueItem
from parser_engine.request import TaskRequest
from scrapy import Request
@TemplateAnnotation(start_url_tpl=({
"name": "youka_shop_listing_api",
"parent": {
"json_key": "data",
},
"fields": [{
"key": "totalPage",
"json_key": "totalPage",
}, {
"key": "ids",
"json_path": "dataList[*].id"
}]
},),
tpls=({
"name": "youka_shop_detail_api",
"itemname": "HuocheDealerItem",
"parent": {
"json_key": "data",
},
"fields": [{
"key": "company_type",
"json_key": "category",
"mapper": {
1: "二手车直营店",
2: "4S店"
}
}, {
"key": "dealer_id",
"json_key": "id",
"required": 1,
}, {
"key": "leads_name",
"json_key": "shopName",
}, {
"key": "area",
"json_path": "districtDto.districtName",
"value_type": "singleton"
}, {
"key": "city",
"json_path": "cityDto.cityName",
"value_type": "singleton"
}, {
"key": "service_phone",
"default_value": "",
}, {
"key": "wechat",
"json_key": "wechat",
}, {
"key": "tags",
"json_key": "tags",
"join": ","
}]
}), channel='youka', leads_src='优卡')
class YoukaSpider(ClueSpider):
name = 'youka'
custom_settings = {
'CONCURRENT_REQUESTS': 2,
'CONCURRENT_REQUESTS_PER_DOMAIN': 1
}
def parse(self, response):
items = self._parse_start_url(response)
meta = response.meta
clue_id = meta.get('clue_id')
from_url = response.request.url
if meta.get('open_pages'):
total_page = items[0]['totalPage']
import re
current_page = int(re.findall('page=(\\d+)', from_url)[0])
for i in range(1, total_page + 1):
if current_page == i:
continue
url = "http://www.china2cv.com/truck-foton-web/api/shop/v1/getShopList?page=%d&pageSize=10" % i
yield ClueItem({"project": "huoche", "spider": self.name, "req": TaskRequest(
url=url,
meta={"from_clue_id": clue_id}
)})
for item in items:
for id in item['ids']:
r = Request(url="http://www.china2cv.com/truck-foton-web/api/shop/v1/getShopInfo?shopId=%d" % int(id),
callback=self._response_downloaded)
r.meta.update(rule=0, from_clue_id=clue_id)
yield r
def process_results(self, response, results):
for item in results:
item['url'] = 'http://www.china2cv.com/storeDetail.html?typess=1&shopId=' + str(item['dealer_id'])
return results完整示例请参考:examples。
解析器
PE向调用方提供一套简单、易懂的参数,实际会将其编译成较为复杂的xpath表达式,再借助scrapy封装的解析器将所需内容提取出来。
返回值
通过配置itemname参数,PE将反射得到所需的Item类,按照配置的映射关系,从提取出的值创建一个Item实例(或者多个),并返回一个可迭代的Item实例列表。
支持表格、列表等形式的批量解析
通过在模板中定义一个父节点,从html页面中的表格、列表等组件中,批量抓取多个同类item
用法示例:使用{"parent": {}, "fields": []}这样的配置,将首先查找匹配parent的节点,然后遍历其每个子节点,对每个子节点应用fields规则,生成一个item。
Clue Mechanism
基于scrapy_redis的线索机制,可持久化(目前支持mysql)线索,方便追踪。
值映射
一个简单的需求场景:API返回的性别字段是0和1,但是需要将其转换成"男"和"女"。
用法示例:原模板的itemname参数通过注解传参,其他的模板参数定义在Item类中,如下所示。
class MyItem(scrapy.Item):
tpl = {"parent": {"xpath":"//div[@id=\"contentDiv\"]//table/tbody/tr[position()>1]"}
name = Field(xpath="//a[@href]/text()")
模板配置文件的位置。默认是parser_engine.json,与scrapy.cfg文件同级。
MySQL配置信息,dict类型,包含以下字段: - DATABASE - USER - PASSWORD - PORT 默认3306 - HOST 默认127.0.0.1
下面的字段在MYSQL配置缺失时生效:
模板配置文件,如parser_engine.json,其构成结构如下:
{
"templates": [
{
"name": "tpl1"
},
{
"name": "tpl2"
}
]
}
模板配置文件中templates列表中的每一项,即为一个模板。
所谓模板,对应的类是PETemplate,其构成结构如下:
所谓字段,对应的类是PEField,属于PE的核心,其构成结构如下:
TemplateAnnotation注解中传进来的参数,除了下面列出的,其他的参数都会被塞到返回值中(当然,如果通过定义itemname,实例化item的时候会静默抛弃那些不属于item的值)。
start_url_tpl: 模板的数组,或者模板id的数组。
对应于start_urls的模板,会生成一个_parse_start_url方法绑定到spider类上,该方法有两个参数(不包括self):
tpls: 模板的数组,或者模板id的数组
具体请参考decorator.py中的注释及源代码。
举个简单的例子。
目标:抓取抖音用户关键字搜索抓包数据分析脚本使用指南页面的几个步骤标题,每个步骤是一个h3标签,步骤标题在id属性里,并且需要去掉形如1-的前缀。 那么相应的配置文件是:
{
"name": "demo",
"fields": [
{
"dom_id": null,
"_css": null,
"xpath": null,
"tags": [
"h3"
],
"classes": [],
"attributes": null,
"position": null,
"key": "步骤",
"value_type": null,
"regexp": "[\\d]{1,2}-(\\w+)",
"attr_name": "id"
}
]
}输出
{'步骤': ['准备工作', '找到电脑的ip地址和端口', '确保手机与电脑建立连接', '抖音搜索关键词', '抓包数据导出', '提取用户信息', '推荐在线转换工具', 'python脚本导出']}
如果只需要第二个步骤,将json配置中的position参数改为2,即可得到如下输出:
{'步骤': ['找到电脑的ip地址和端口']}
{
"name": "json-api-demo",
"fields": [
{
"key": "poi_id",
"json_path": "$.pois[:1].id"
},
{
"key": "地名",
"json_path": "$.data.name",
"value_type": "singleton"
},
{
"key": "下级",
"json_path": "$.data.children[*].name"
}
]
}由于json_path解析总是返回一个list,对于一些确定的字段,比如通过调用APIhttp://172.31.1.4:30815/api/dict/area/0?childrenDepth=1,想拿到该地区的name字段,则可以设置value_type为singleton,则PE会做一次转换。
| Back | FazBrowse Home | New Git URL |