Scrapy模型助手,用于从模型创建爬虫
项目描述
使用Scrapy选择器创建爬虫
============================================
[](https://travis-ci.org/rochacbruno/scrapy_model)
[](https://pypi.python.org/pypi/scrapy_model/)
[](https://pypi.python.org/pypi/scrapy_model/)
## 什么是Scrapy?
Scrapy是一个快速的高级网页抓取和爬虫框架,用于爬取网站并从其页面中提取结构化数据。它可以用于各种目的,从数据挖掘到监控和自动化测试。
https://scrapy.net.cn/
## 什么是scrapy_model?
它只是一个助手,用于使用Scrapy选择器创建爬虫,允许您通过CSS或通过XPATH选择元素,并通过模型(就像ORM模型一样)结构化您的爬虫,并通过`populate`方法将其连接到ORM模型。
导入BaseFetcherModel,CSSField或XPathField(您可以使用两者)
```python
from scrapy_model import BaseFetcherModel, CSSField
```
转到您想要抓取的网页,并使用Chrome开发者工具或Firebug找出CSS路径。假设您想从某个页面获取以下片段。
```html
<span id="person">Bruno Rocha <a href="http://brunorocha.org">网站</a></span>
```
```python
class MyFetcher(BaseFetcherModel)
name = CSSField('span#person')
website = CSSField('span#person a')
# XPathField('//xpath_selector_here')
```
字段可以接收``auto_extract=True``参数,在调用解析器或处理器之前自动从选择器中提取值。您还可以传递``takes_first=True``,它将尝试自动提取结果的第一元素,因为Scrapy选择器返回匹配元素的一个列表。
### 单个字段中的多个查询
您可以为单个字段使用多个查询
```python
name = XPathField(
['//*[@id="8"]/div[2]/div/div[2]/div[2]/ul',
'//*[@id="8"]/div[2]/div/div[3]/div[2]/ul']
)
```
在这种情况下,解析器将尝试通过第一个查询来获取数据,如果找到匹配项,则返回,否则将尝试后续查询,直到找到某个匹配项,或者返回一个空选择器。
#### 通过查询验证器找到最佳匹配
如果您想运行多个查询并验证最佳匹配,可以传递一个验证器函数,它将接收Scrapy选择器并返回一个布尔值。
例如,假设您定义了上面的“name”字段,并且想验证每个查询以确保其中包含文本“Schblaums”的“li”。
```python
def has_schblaums(selector)
for li in selector.css('li'): # 选择ul选择器内的每个<li>
li_text = li.css('::text').extract() # 仅提取文本
if "Schblaums" in li_text: # 检查是否包含“Schblaums”
return True # 返回已验证!
return False # 否则所有查询均无效
class Fetcher(....)
name = XPathField(
['//*[@id="8"]/div[2]/div/div[2]/div[2]/ul',
'//*[@id="8"]/div[2]/div/div[3]/div[2]/ul'],
query_validator=has_schblaums,
default="undefined_name" # 可选
)
```
在上面的示例中,如果两个查询都无效,则“name”字段将填充为empty_selector,或者“default”参数中定义的值。
> **注意**:如果字段有“default”并且所有匹配器都失败,则默认值将被传递到“processor”和“parse_”方法。
每个名为``parse_<field>``的方法将在每个字段的字段都获取后运行。
```python
def parse_name(self, selector)
# 这里selector是'span#person'的Scrapy选择器
name = selector.css('::text').extract()
return name
def parse_website(self, selector)
# 这里selector是'span#person a'的Scrapy选择器
website_url = selector.css('::attr(href)').extract()
return website_url
```
定义后需要运行爬虫
```python
fetcher = Myfetcher(url='http://.....') # 可选:使用cached_fetch=True将请求缓存到redis
fetcher.parse()
```
现在您可以在fetcher中迭代``_data``、``_raw_data``和属性
```python
>>> fetcher.name
<CSSField - name - Bruno Rocha>
>>> fetcher.name.value
Bruno Rocha
>>> fetcher._data
{"name": "Bruno Rocha", "website": "http://brunorocha.org"}
```
您可以填充一些对象
```python
>>> obj = MyObject()
>>> fetcher.populate(obj) # 可选:字段
>>> obj.name
Bruno Rocha
```
如果您不想在类中显式定义每个字段,可以使用JSON文件来自动化过程
```python
class MyFetcher(BaseFetcherModel)
"""将从json中加载"""
fetcher = MyFetcher(url='http://.....')
fetcher.load_mappings_from_file('path/to/file.json')
fetcher.parse()
```
在这种情况下,file.json应该是
```json
{
"name": {"css", "span#person"},
"website": {"css": "span#person a"}
}
```
您可以使用``{"xpath": "..."}``如果您更愿意使用xpath进行选择
### 解析器和处理器
对于每个字段,有两种方式来转换或规范化数据
#### 处理器
处理器是一个函数,或是一系列按给定顺序调用的函数,它将对字段值执行操作,接收原始选择器或值(取决于auto_extract和takes_first参数)。
它可以用于规范化、清洁、转换等。
示例
```python
def normalize_state(state_name)
# 查询数据库并返回第一个state对象的实例
return MyDatabase.State.Search(name=state_name).first()
def text_cleanup(state_name)
return state_name.strip().replace('-', '').lower()
class MyFetcher(BaseFetcherModel)
state = CSSField(
"#state::text",
takes_first=True,
processor=[text_cleanup, normalize_state]
)
fetcher = MyFetcher(url="http://....")
fetcher.parse()
fetcher._raw_data.state
'Sao-Paulo'
fetcher._data.state
<ORM实例 - State - São Paulo>
```
#### 解析方法
任何名为`parse_`的方法将在选择和解析的所有过程之后运行,它接收选择器或值(取决于auto_extract和该字段的take_first参数)。
示例
```python
def parse_name(self, selector)
return selector.css('::text').extract()[0].upper()
```
在上面的例子中,name字段返回原始选择器,在解析方法中,我们可以使用`css`或`xpath`构建额外的查询,并且需要从选择器中提取值,并根据需要选择第一个元素并应用所需的任何转换。
### 缓存HTML获取
为了缓存从URL获取的HTML以供未来的解析和测试,您需要指定一个缓存模型。默认情况下没有缓存,但您可以使用内置的RedisCache。
```python
from scrapy_model import RedisCache
fetcher = TestFetcher(cache_fetch=True,
cache=RedisCache,
cache_expire=1800)
```
或指定Redis客户端的参数。
> 这是python ``redis``模块的一般Redis连接
```python
fetcher = TestFetcher(cache_fetch=True,
cache=RedisCache("192.168.0.12:9200"),
cache_expire=1800)
```
您可以根据需要创建自己的缓存结构,例如:在memcached或s3中缓存HTML
缓存类只需要实现`get`和`set`方法。
```python
from boto import connect_s3
class S3Cache(object)
def __init__(self, *args, **kwargs)
connection = connect_s3(ACCESS_KEY, SECRET_KEY)
self.bucket = connection.get_bucket(BUCKET_ID)
def get(self, key)
value = self.bucket.get_key(key)
return value.get_contents_as_string() if key else None
def set(self, key, value, expire=None)
self.bucket.set_contents(key, value, expire=expire)
fetcher = MyFetcher(url="http://...",
cache_fetch=True,
cache=S3cache,
cache_expire=1800)
```
### 安装
安装简单
如果正在运行ubuntu,可能需要运行
```bash
sudo apt-get install python-scrapy
sudo apt-get install libffi-dev
sudo apt-get install python-dev
```
然后
```bash
pip install scrapy_model
```
或
```bash
git clone https://github.com/rochacbruno/scrapy_model
cd scrapy_model
pip install -r requirements.txt
python setup.py install
python example.py
```
示例代码以获取URL http://en.m.wikipedia.org/wiki/Guido_van_Rossum
```python
#coding: utf-8
from scrapy_model import BaseFetcherModel, CSSField, XPathField
class TestFetcher(BaseFetcherModel)
photo_url = XPathField('//*[@id="content"]/div[1]/table/tr[2]/td/a')
nationality = CSSField(
"#content > div:nth-child(1) > table > tr:nth-child(4) > td > a",
)
links = CSSField(
"#content > div:nth-child(11) > ul > li > a.external::attr(href)",
auto_extract=True
)
def parse_photo_url(self, selector)
return "http://en.m.wikipedia.org/{}".format(
selector.xpath("@href").extract()[0]
)
def parse_nationality(self, selector)
return selector.css("::text").extract()[0]
def parse_name(self, selector)
return selector.extract()[0]
def pre_parse(self, selector=None)
# 这个方法在解析之前执行
# 您可以覆盖它,请参阅文档字符串
def post_parse(self)
# 解析后执行
# 您可以在self._data上加载任何数据
# 访问self._data和self._fields以获取当前数据
# self.selector包含原始页面
# self.fetch()返回原始HTML
self._data.url = self.url
class DummyModel(object)
"""
仅用于测试,它可以是你数据库ORM中的模型
"""
if __name__ == "__main__"
from pprint import pprint
fetcher = TestFetcher(cache_fetch=True)
fetcher.url = "http://en.m.wikipedia.org/wiki/Guido_van_Rossum"
# 映射可以从json文件加载
# fetcher.load_mappings_from_file('path/to/file')
fetcher.mappings['name'] = {
"css": ("#section_0::text")
}
fetcher.parse()
打印 "Fetcher 持有数据"
打印 fetcher._data.name
打印 fetcher._data
# 如何填充一个对象
打印 "填充一个对象"
dummy = DummyModel()
fetcher.populate(dummy, fields=["name", "nationality"])
# fields 属性是可选的
打印 dummy.nationality
pprint(dummy.__dict__)
```
# 输出
```
Fetcher 持有数据
Guido van Rossum
{'links': [u'https://pythonlang.cn/~guido/',
u'http://neopythonic.blogspot.com/',
u'http://www.artima.com/weblogs/index.jsp?blogger=guido',
u'http://python-history.blogspot.com/',
u'https://pythonlang.cn/doc/essays/cp4e.html',
u'http://www.twit.tv/floss11',
u'http://www.computerworld.com.au/index.php/id;66665771',
u'http://www.stanford.edu/class/ee380/Abstracts/081105.html',
u'http://stanford-online.stanford.edu/courses/ee380/081105-ee380-300.asx'],
'name': u'Guido van Rossum',
'nationality': u'Dutch',
'photo_url': 'http://en.m.wikipedia.org//wiki/File:Guido_van_Rossum_OSCON_2006.jpg',
'url': 'http://en.m.wikipedia.org/wiki/Guido_van_Rossum'}
填充一个对象
荷兰
{'name': u'Guido van Rossum', 'nationality': u'Dutch'}
```
============================================
[](https://travis-ci.org/rochacbruno/scrapy_model)
[](https://pypi.python.org/pypi/scrapy_model/)
[](https://pypi.python.org/pypi/scrapy_model/)
## 什么是Scrapy?
Scrapy是一个快速的高级网页抓取和爬虫框架,用于爬取网站并从其页面中提取结构化数据。它可以用于各种目的,从数据挖掘到监控和自动化测试。
https://scrapy.net.cn/
## 什么是scrapy_model?
它只是一个助手,用于使用Scrapy选择器创建爬虫,允许您通过CSS或通过XPATH选择元素,并通过模型(就像ORM模型一样)结构化您的爬虫,并通过`populate`方法将其连接到ORM模型。
导入BaseFetcherModel,CSSField或XPathField(您可以使用两者)
```python
from scrapy_model import BaseFetcherModel, CSSField
```
转到您想要抓取的网页,并使用Chrome开发者工具或Firebug找出CSS路径。假设您想从某个页面获取以下片段。
```html
<span id="person">Bruno Rocha <a href="http://brunorocha.org">网站</a></span>
```
```python
class MyFetcher(BaseFetcherModel)
name = CSSField('span#person')
website = CSSField('span#person a')
# XPathField('//xpath_selector_here')
```
字段可以接收``auto_extract=True``参数,在调用解析器或处理器之前自动从选择器中提取值。您还可以传递``takes_first=True``,它将尝试自动提取结果的第一元素,因为Scrapy选择器返回匹配元素的一个列表。
### 单个字段中的多个查询
您可以为单个字段使用多个查询
```python
name = XPathField(
['//*[@id="8"]/div[2]/div/div[2]/div[2]/ul',
'//*[@id="8"]/div[2]/div/div[3]/div[2]/ul']
)
```
在这种情况下,解析器将尝试通过第一个查询来获取数据,如果找到匹配项,则返回,否则将尝试后续查询,直到找到某个匹配项,或者返回一个空选择器。
#### 通过查询验证器找到最佳匹配
如果您想运行多个查询并验证最佳匹配,可以传递一个验证器函数,它将接收Scrapy选择器并返回一个布尔值。
例如,假设您定义了上面的“name”字段,并且想验证每个查询以确保其中包含文本“Schblaums”的“li”。
```python
def has_schblaums(selector)
for li in selector.css('li'): # 选择ul选择器内的每个<li>
li_text = li.css('::text').extract() # 仅提取文本
if "Schblaums" in li_text: # 检查是否包含“Schblaums”
return True # 返回已验证!
return False # 否则所有查询均无效
class Fetcher(....)
name = XPathField(
['//*[@id="8"]/div[2]/div/div[2]/div[2]/ul',
'//*[@id="8"]/div[2]/div/div[3]/div[2]/ul'],
query_validator=has_schblaums,
default="undefined_name" # 可选
)
```
在上面的示例中,如果两个查询都无效,则“name”字段将填充为empty_selector,或者“default”参数中定义的值。
> **注意**:如果字段有“default”并且所有匹配器都失败,则默认值将被传递到“processor”和“parse_”方法。
每个名为``parse_<field>``的方法将在每个字段的字段都获取后运行。
```python
def parse_name(self, selector)
# 这里selector是'span#person'的Scrapy选择器
name = selector.css('::text').extract()
return name
def parse_website(self, selector)
# 这里selector是'span#person a'的Scrapy选择器
website_url = selector.css('::attr(href)').extract()
return website_url
```
定义后需要运行爬虫
```python
fetcher = Myfetcher(url='http://.....') # 可选:使用cached_fetch=True将请求缓存到redis
fetcher.parse()
```
现在您可以在fetcher中迭代``_data``、``_raw_data``和属性
```python
>>> fetcher.name
<CSSField - name - Bruno Rocha>
>>> fetcher.name.value
Bruno Rocha
>>> fetcher._data
{"name": "Bruno Rocha", "website": "http://brunorocha.org"}
```
您可以填充一些对象
```python
>>> obj = MyObject()
>>> fetcher.populate(obj) # 可选:字段
>>> obj.name
Bruno Rocha
```
如果您不想在类中显式定义每个字段,可以使用JSON文件来自动化过程
```python
class MyFetcher(BaseFetcherModel)
"""将从json中加载"""
fetcher = MyFetcher(url='http://.....')
fetcher.load_mappings_from_file('path/to/file.json')
fetcher.parse()
```
在这种情况下,file.json应该是
```json
{
"name": {"css", "span#person"},
"website": {"css": "span#person a"}
}
```
您可以使用``{"xpath": "..."}``如果您更愿意使用xpath进行选择
### 解析器和处理器
对于每个字段,有两种方式来转换或规范化数据
#### 处理器
处理器是一个函数,或是一系列按给定顺序调用的函数,它将对字段值执行操作,接收原始选择器或值(取决于auto_extract和takes_first参数)。
它可以用于规范化、清洁、转换等。
示例
```python
def normalize_state(state_name)
# 查询数据库并返回第一个state对象的实例
return MyDatabase.State.Search(name=state_name).first()
def text_cleanup(state_name)
return state_name.strip().replace('-', '').lower()
class MyFetcher(BaseFetcherModel)
state = CSSField(
"#state::text",
takes_first=True,
processor=[text_cleanup, normalize_state]
)
fetcher = MyFetcher(url="http://....")
fetcher.parse()
fetcher._raw_data.state
'Sao-Paulo'
fetcher._data.state
<ORM实例 - State - São Paulo>
```
#### 解析方法
任何名为`parse_
示例
```python
def parse_name(self, selector)
return selector.css('::text').extract()[0].upper()
```
在上面的例子中,name字段返回原始选择器,在解析方法中,我们可以使用`css`或`xpath`构建额外的查询,并且需要从选择器中提取值,并根据需要选择第一个元素并应用所需的任何转换。
### 缓存HTML获取
为了缓存从URL获取的HTML以供未来的解析和测试,您需要指定一个缓存模型。默认情况下没有缓存,但您可以使用内置的RedisCache。
```python
from scrapy_model import RedisCache
fetcher = TestFetcher(cache_fetch=True,
cache=RedisCache,
cache_expire=1800)
```
或指定Redis客户端的参数。
> 这是python ``redis``模块的一般Redis连接
```python
fetcher = TestFetcher(cache_fetch=True,
cache=RedisCache("192.168.0.12:9200"),
cache_expire=1800)
```
您可以根据需要创建自己的缓存结构,例如:在memcached或s3中缓存HTML
缓存类只需要实现`get`和`set`方法。
```python
from boto import connect_s3
class S3Cache(object)
def __init__(self, *args, **kwargs)
connection = connect_s3(ACCESS_KEY, SECRET_KEY)
self.bucket = connection.get_bucket(BUCKET_ID)
def get(self, key)
value = self.bucket.get_key(key)
return value.get_contents_as_string() if key else None
def set(self, key, value, expire=None)
self.bucket.set_contents(key, value, expire=expire)
fetcher = MyFetcher(url="http://...",
cache_fetch=True,
cache=S3cache,
cache_expire=1800)
```
### 安装
安装简单
如果正在运行ubuntu,可能需要运行
```bash
sudo apt-get install python-scrapy
sudo apt-get install libffi-dev
sudo apt-get install python-dev
```
然后
```bash
pip install scrapy_model
```
或
```bash
git clone https://github.com/rochacbruno/scrapy_model
cd scrapy_model
pip install -r requirements.txt
python setup.py install
python example.py
```
示例代码以获取URL http://en.m.wikipedia.org/wiki/Guido_van_Rossum
```python
#coding: utf-8
from scrapy_model import BaseFetcherModel, CSSField, XPathField
class TestFetcher(BaseFetcherModel)
photo_url = XPathField('//*[@id="content"]/div[1]/table/tr[2]/td/a')
nationality = CSSField(
"#content > div:nth-child(1) > table > tr:nth-child(4) > td > a",
)
links = CSSField(
"#content > div:nth-child(11) > ul > li > a.external::attr(href)",
auto_extract=True
)
def parse_photo_url(self, selector)
return "http://en.m.wikipedia.org/{}".format(
selector.xpath("@href").extract()[0]
)
def parse_nationality(self, selector)
return selector.css("::text").extract()[0]
def parse_name(self, selector)
return selector.extract()[0]
def pre_parse(self, selector=None)
# 这个方法在解析之前执行
# 您可以覆盖它,请参阅文档字符串
def post_parse(self)
# 解析后执行
# 您可以在self._data上加载任何数据
# 访问self._data和self._fields以获取当前数据
# self.selector包含原始页面
# self.fetch()返回原始HTML
self._data.url = self.url
class DummyModel(object)
"""
仅用于测试,它可以是你数据库ORM中的模型
"""
if __name__ == "__main__"
from pprint import pprint
fetcher = TestFetcher(cache_fetch=True)
fetcher.url = "http://en.m.wikipedia.org/wiki/Guido_van_Rossum"
# 映射可以从json文件加载
# fetcher.load_mappings_from_file('path/to/file')
fetcher.mappings['name'] = {
"css": ("#section_0::text")
}
fetcher.parse()
打印 "Fetcher 持有数据"
打印 fetcher._data.name
打印 fetcher._data
# 如何填充一个对象
打印 "填充一个对象"
dummy = DummyModel()
fetcher.populate(dummy, fields=["name", "nationality"])
# fields 属性是可选的
打印 dummy.nationality
pprint(dummy.__dict__)
```
# 输出
```
Fetcher 持有数据
Guido van Rossum
{'links': [u'https://pythonlang.cn/~guido/',
u'http://neopythonic.blogspot.com/',
u'http://www.artima.com/weblogs/index.jsp?blogger=guido',
u'http://python-history.blogspot.com/',
u'https://pythonlang.cn/doc/essays/cp4e.html',
u'http://www.twit.tv/floss11',
u'http://www.computerworld.com.au/index.php/id;66665771',
u'http://www.stanford.edu/class/ee380/Abstracts/081105.html',
u'http://stanford-online.stanford.edu/courses/ee380/081105-ee380-300.asx'],
'name': u'Guido van Rossum',
'nationality': u'Dutch',
'photo_url': 'http://en.m.wikipedia.org//wiki/File:Guido_van_Rossum_OSCON_2006.jpg',
'url': 'http://en.m.wikipedia.org/wiki/Guido_van_Rossum'}
填充一个对象
荷兰
{'name': u'Guido van Rossum', 'nationality': u'Dutch'}
```
项目详情
下载文件
下载适用于您的平台文件。如果您不确定选择哪个,请了解更多关于 安装包 的信息。
源分布
scrapy_model-0.1.5.tar.gz (10.7 kB 查看散列)
构建分布
scrapy_model-0.1.5-py2.py3-none-any.whl (13.1 kB 查看散列)
关闭
scrapy_model-0.1.5.tar.gz 的散列
算法 | 散列摘要 | |
---|---|---|
SHA256 | b93fa181a54c2b6ec4ba084a0496c0e5774d03b5040297dcbe49d874035ec458 |
|
MD5 | 9710f8dbbfd523421ef5cf871c8d6407 |
|
BLAKE2b-256 | 55bf162f87f887bdb5c5644155a181e429ef02f339c1125b4b3594603e389f81 |
关闭
scrapy_model-0.1.5-py2.py3-none-any.whl 的散列
算法 | 散列摘要 | |
---|---|---|
SHA256 | 13fa30859570d35c2e1dc5cc543380a057f9decdb339fbe41126e8c77a8fed30 |
|
MD5 | 40908fea7007215d7bb6e2bb208c97e8 |
|
BLAKE2b-256 | 9128ab5db86ee73a6f5a515131f27b59977147acb0365f930fb8b8cf61200f0a |