爬虫scrapy
爬虫scrapy
爬虫scrapy
点击这里还可以去我的博客主页o
点击这里还可以去大神的学习笔记o
本页只是为了方便本人以后复习爬虫scrapy用的笔记markdown
纯属娱乐,如有雷同,打死不认——
![](https://images2017.cnblogs.com/blog/764024/201802/764024-20180205162555654-1350503259.jpg)
- 开启scrapy之旅
- 常用的工具命令
- 项目开始以及连接mongodb
- scrapy选择器xpath和css选择器
- scrapy框架实现篇
- scrapy避免被禁止
- 自动爬取网页实战
- crawlspider快速抓取
1.安装好虚拟环境virtualenv scrapy_env
进入scripts然后activate进入虚拟
2.下载相应的包
分别是顺序pip intsall
wheel,lxml,PyOpenssl,Twisted,Pywin32,Scrapy
3.开始scrapy项目 scrapy startproject quote
4.然后scrapy genspider quotes quotes.toscrape.com
打开pycharm打开相应文件夹,里面就是这个项目quote,里面有同名quote然后里面spiders还有自己创建的quotes
遇到的问题
因为创建了quote再pychram里面打开终端就直接打开了quote一个虚拟环境,然后执行scrapy怎样都是'scrapy' 不是内部或外部命令,也不是可运行的程序
百度半天说没有加入path,然后我吧D:\Python_code\scrapy_env\Scripts加入环境了后发现进入quote这个虚拟也不行,'scrapy' 不是内部或外部命令,也不是可运行的程序,然后我想起来,这个是虚拟环境,加入path也不能进入这个虚拟,所以我退出虚拟quote,deactivate进入scrapy_env的虚拟即scrapy的安装虚拟路径和环境再执行,ok了
点击这里还可以去scrapy官方文档
点击这里去教程scrapy菜鸟教程
scrapy 中常用的工具分两种1是全局命令,2是项目命令
全局命令就是不依靠Scrapy就能直接运行,而项目命令就是需要在项目中才能运行
1全局命令 我们可以通过scrapy -h直接查看所有的全局命令
2项目命令基于项目使用,进入项目才能使用
第一是进入scrapy_env的虚拟,因为我的scrapy和一系列的包都在这个虚拟下载的,不是在全局下载的,当时pip的时候就在虚拟pip,这样呢以后每个项目都能用scrapy的包啊虚拟这些,麻烦的就是每次需要在终端进入这个虚拟才行
首先在item中定义好数据
class QuoteItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
text=scrapy.Field()
author=scrapy.Field()
tags=scrapy.Field()
然后在spiders目录下的quotes中
def parse(self, response):
quotes=response.css('.quote')#.quote是pyquery的特点
for quote in quotes.css('.text::text').extract_frist():
item=QuoteItem()
text=quote.css('.text::text').extract_frist()
author=quote.css('.author::text').extract_frist()
tags=quote.css('.tags .tag::text').extract() #所有结果,多个内容,类似于findall
item['text'] = text
item['author']=author
item['tags']=tags
yield item
next =response.css('.pager .next a::attr(href)').extract_first() #这个是参看f12里面下一页按钮那里的的 是两个class
url=response.urljoin(next) #urljoin是一个方法,可以生成一个完整的url因为是不完整的比如少http://等
yield scrapy.Request(url=url,callback=self.parse) #递归,自己回调自己
解释下.quote .text这是一种css的选择方式而::text则是scrapy的方式,表示输出data的字符串,而直接用.css的话输出为标签,
extract表示输出返回的是一个列表,_frist则就是输出第一个标签没有frist就算全部
然后在终端,如果在Shell因为我一开始写了命令crapy shell quotes.toscrapy.com进入了这个交互环境中,所以执行exit()先退到scrapy_env的虚拟当中再执行scrapy crawl quotes(quotes是那个文件夹名)就 ok了
如果最后执行 scrapy crawl quotes没问题后要想保存数据可以scrapy crawl quotes -0 quotes.json就保存为json格式了同样的其他格式也是这样比如scrapy crawl quotes -0 quotes.csv等就会发现有这个文件了,里面数据也在里面格式也更加清楚
在pipeline里面
from scrapy.exceptions import DropItem
import pymongo
class TextPipeline(object):
def __init__(self):
self.limit=50
def process_item(self, item, spider):
if item['text']:
if len (item['text'])>self.limit:
item['text']=item['text'][0:self.limit].rstrip()+'........' #rstrip去除空格
return item
else:
return DropItem('miss Text')
class mongopipeline(object):
def __init__(self,mongo_url,mongo_db):
self.mongo_url=mongo_url
self.mongo_db=mongo_db
@classmethod
def from_crawler(cls,crawler):
return cls(
mongo_url=crawler.setting.get('MONGO_URL'),
mongo_db=crawler.setting.get('MONGO_DB'),
)
def open_spider(self,spider):
self.client=pymongo.MongoClient(self.mongo_url)
self.db=self.client(self.mongo_db)
def process_item(self,item,spider):
name=item.__class__.__name__
self.db[name].insert(dict(item))
return item
def close_spider(self,spider):
self.client.close()
而我的电脑连接mongodb两种方法 ,第一种直接用命令mongod--dbpath E:\data或者直接搜索服务找到mondodb server 打开即可
而且在MongoDB中经常我错的地方就是
修改完成后的代码:
client = pymongo.MongoClient('localhost')
db = client['my_database']#注意这里用中括号!!
不然总会'MongoClient' object is not callable
我们将在下面的例子中使用这个 XML 文档。
<?xml version="1.0" encoding="ISO-8859-1"?>
<bookstore>
<book>
<title lang="eng">Harry Potter</title>
<price>29.99</price>
</book>
<book>
<title lang="eng">Learning XML</title>
<price>39.95</price>
</book>
</bookstore>
选取节点
XPath 使用路径表达式在 XML 文档中选取节点。节点是通过沿着路径或者 step 来选取的。
下面列出了最有用的路径表达式:
表达式 描述
nodename 选取此节点的所有子节点。
/ 从根节点选取。
// 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。
. 选取当前节点。
.. 选取当前节点的父节点。
@ 选取属性。
实例
在下面的表格中,我们已列出了一些路径表达式以及表达式的结果:
路径表达式 结果
bookstore 选取 bookstore 元素的所有子节点。
/bookstore
选取根元素 bookstore。
注释:假如路径起始于正斜杠( / ),则此路径始终代表到某元素的绝对路径!
bookstore/book 选取属于 bookstore 的子元素的所有 book 元素。
//book 选取所有 book 子元素,而不管它们在文档中的位置。
bookstore//book 选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置。
//@lang 选取名为 lang 的所有属性。
谓语(Predicates)
谓语用来查找某个特定的节点或者包含某个指定的值的节点。
谓语被嵌在方括号中。
实例
在下面的表格中,我们列出了带有谓语的一些路径表达式,以及表达式的结果:
路径表达式 结果
/bookstore/book[1] 选取属于 bookstore 子元素的第一个 book 元素。
/bookstore/book[last()] 选取属于 bookstore 子元素的最后一个 book 元素。
/bookstore/book[last()-1] 选取属于 bookstore 子元素的倒数第二个 book 元素。
/bookstore/book[position()<3] 选取最前面的两个属于 bookstore 元素的子元素的 book 元素。
//title[@lang] 选取所有拥有名为 lang 的属性的 title 元素。
//title[@lang='eng'] 选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性。
/bookstore/book[price>35.00] 选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00。
/bookstore/book[price>35.00]/title 选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00。
选取未知节点
XPath 通配符可用来选取未知的 XML 元素。
/html/body/h2/text() 即 提取出h2便签的内容
使用//可以提取某个标签的所有信息
//p 选取所有p标签的所有信息
加入很多段 <img src='......' class='f1'>
这时
//img[@class='f1']就是获取所有class属性值为f1的<img>标签的内容
通配符 描述
* 匹配任何元素节点。
@* 匹配任何属性节点。
node() 匹配任何类型的节点。
实例
在下面的表格中,我们列出了一些路径表达式,以及这些表达式的结果:
路径表达式 结果
/bookstore/* 选取 bookstore 元素的所有子元素。
//* 选取文档中的所有元素。
//title[@*] 选取所有带有属性的 title 元素。
选取若干路径
通过在路径表达式中使用“|”运算符,您可以选取若干个路径。
实例
在下面的表格中,我们列出了一些路径表达式,以及这些表达式的结果:
路径表达式 结果
//book/title | //book/price 选取 book 元素的所有 title 和 price 元素。
//title | //price 选取文档中的所有 title 和 price 元素。
/bookstore/book/title | //price 选取属于 bookstore 元素的 book 元素的所有 title 元素,以及文档中所有的 price 元素。
scrapy中的css选择器语法
*
所有节点
#container
选择id为container的节点
.container
选择class为container的节点
li a
选择所有li下的所有a节点
ul + p
选择ul后的第一个p节点
div#container > ul
选择id为container的div的第一个ul子节点
ul ~ p
选取与ul相邻的所有的p元素
a[title]
选取所有包含有title属性的a元素
a[href="http://jobbole.com"]
选取所有href属性为jobbole.com的a元素
a[href*="jobbole"]
选取所有href属性包含jobbole的a元素
a[href^="http"]
选取所有href属性以http开头的a元素
a[href$=".jpt"]
选取所有href属性以.jpg结尾的a元素
input[type=radio]:checked
选取选中的radio元素
div:not(#container)
选取所有id不为container的div元素
li:nth-child(3)
选取第三个li元素
tr:nth-child(2n)
第偶数个tr
scrapy选择器实战
Scrapy选择器构建于 lxml 库之上,这意味着它们在速度和解析准确性上非常相似。
我们将使用 Scrapy shell
(提供交互测试)和位于Scrapy文档服务器的一个样例页面,来解释如何使用选择器:
http://doc.scrapy.org/en/latest/_static/selectors-sample1.html
这里是它的HTML源码:
<html>
<head>
<base href='http://example.com/' />
<title>Example website</title>
</head>
<body>
<div id='images'>
<a href='image1.html'>Name: My image 1 <br />![](image1_thumb.jpg)</a>
<a href='image2.html'>Name: My image 2 <br />![](image2_thumb.jpg)</a>
<a href='image3.html'>Name: My image 3 <br />![](image3_thumb.jpg)</a>
<a href='image4.html'>Name: My image 4 <br />![](image4_thumb.jpg)</a>
<a href='image5.html'>Name: My image 5 <br />![](image5_thumb.jpg)</a>
</div>
</body>
</html>
3.1 构造选择器
首先, 我们打开shell:
scrapy shell http://doc.scrapy.org/en/latest/_static/selectors-sample1.html
接着,当shell载入后,您将获得名为response
的shell变量,其为响应的response, 并且在其 response.selector属性上绑定了一个 selector。
因为我们处理的是HTML,选择器将自动使用HTML语法分析。
那么,通过查看 HTML code 该页面的源码,我们构建一个XPath来选择title标签内的文字:
>>> response.selector.xpath('//title/text()')
[<Selector (text) xpath=//title/text()>]
由于在response中使用XPath、CSS查询十分普遍,因此,Scrapy提供了两个实用的快捷方式: response.xpath() 及 response.css():
>>> response.xpath('//title/text()')
[<Selector (text) xpath=//title/text()>]
>>> response.css('title::text')
[<Selector (text) xpath=//title/text()>]
如你所见, .xpath()及 .css()方法返回一个类 SelectorList 的实例, 它是一个新选择器的列表。这个API可以用来快速的提取嵌套数据。
为了提取真实的原文数据,你需要调用 .extract()方法如下:
>>> response.xpath('//title/text()').extract()
[u'Example website']
如果想要提取到第一个匹配到的元素, 必须调用 .extract_first() selector:
>>> response.xpath('//div[@id="images"]/a/text()').extract_first()
u'Name: My image 1 '
现在我们将得到根URL(base URL)和一些图片链接:
>>> response.xpath('//base/@href').extract()
[u'http://example.com/']
>>> response.css('base::attr(href)').extract()
[u'http://example.com/']
>>> response.xpath('//a[contains(@href, "image")]/@href').extract()
[u'image1.html',
u'image2.html',
u'image3.html',
u'image4.html',
u'image5.html']
>>> response.css('a[href*=image]::attr(href)').extract()
[u'image1.html',
u'image2.html',
u'image3.html',
u'image4.html',
u'image5.html']
>>> response.xpath('//a[contains(@href, "image")]/img/@src').extract()
[u'image1_thumb.jpg',
u'image2_thumb.jpg',
u'image3_thumb.jpg',
u'image4_thumb.jpg',
u'image5_thumb.jpg']
>>> response.css('a[href*=image] img::attr(src)').extract()
[u'image1_thumb.jpg',
u'image2_thumb.jpg',
u'image3_thumb.jpg',
u'image4_thumb.jpg',
u'image5_thumb.jpg']
3.2选择器嵌套
选择器方法( .xpath() or .css() )返回相同类型的选择器列表,因此你也可以对这些选择器调用选择器方法。下面是一个例子:
>>> links = response.xpath('//a[contains(@href, "image")]')
>>> links.extract()
[u'<a href="image1.html">Name: My image 1 <br>![](image1_thumb.jpg)</a>',
u'<a href="image2.html">Name: My image 2 <br>![](image2_thumb.jpg)</a>',
u'<a href="image3.html">Name: My image 3 <br>![](image3_thumb.jpg)</a>',
u'<a href="image4.html">Name: My image 4 <br>![](image4_thumb.jpg)</a>',
u'<a href="image5.html">Name: My image 5 <br>![](image5_thumb.jpg)</a>']
>>> for index, link in enumerate(links):
args = (index, link.xpath('@href').extract(), link.xpath('img/@src').extract())
print 'Link number %d points to url %s and image %s' % args
Link number 0 points to url [u'image1.html'] and image [u'image1_thumb.jpg']
Link number 1 points to url [u'image2.html'] and image [u'image2_thumb.jpg']
Link number 2 points to url [u'image3.html'] and image [u'image3_thumb.jpg']
Link number 3 points to url [u'image4.html'] and image [u'image4_thumb.jpg']
Link number 4 points to url [u'image5.html'] and image [u'image5_thumb.jpg']
3.3 结合正则表达式使用选择器(selectors)
Selector 也有一个 .re()方法,用来通过正则表达式来提取数据。然而,不同于使用 .xpath() 或者 .css() 方法, .re() 方法返回unicode字符串的列表。所以你无法构造嵌套式的 .re() 调用。
下面是一个例子,从上面的 HTML code 中提取图像名字:
>>> response.xpath('//a[contains(@href, "image")]/text()').re(r'Name:\s*(.*)')
[u'My image 1',
u'My image 2',
u'My image 3',
u'My image 4',
u'My image 5']
另外还有一个糅合了 .extract_first() 与 .re() 的函数 .re_first() . 使用该函数可以提取第一个匹配到的字符串:
>>> response.xpath('//a[contains(@href, "image")]/text()').re_first(r'Name:\s*(.*)')
u'My image 1'
创建一个项目scrapy startproject name
Items实战
进入items 其实items就是容器,保存爬取的数据,储蓄作用
创建的items会加上你项目的名称自动创建类
如
class HellScrapyItem(scrapy.Item):#继承scrapy下的Item类
# define the fields for your item here like:
# name = scrapy.Field()
urlname=scrapy.Field()
urlkey=scrapy.Field()
urlcr=scrapy.Field()
urladdr=scrapy.Field()
这个类里面定义结构化数据,即将scrapy下的Field类实例化
然后我们要用的时候实例化这个类就可以了
如:weisuen=person(urlname='weiwei',urlkey='123',urlcr='sdda',urladdr='asd')
然后print(weisuen)就有了
parse方法
默认的回调函数
就是很多函数返回的时候有一个callback=
就是这个
Spider编写
scrapy.spiders.Spider
这是最简单的蜘蛛,也是每个其他蜘蛛必须继承的蜘蛛(包括与Scrapy捆绑在一起的蜘蛛,以及你自己编写的蜘蛛)。它不提供任何特殊功能。它只提供了一个默认start_requests()实现,它从start_urlsspider属性发送请求,并parse 为每个结果响应调用spider的方法。
而我们可以通过项目中运行genspider命令创建一个爬虫文件
scrapy genspider weisuen iqianyue.com
然后就有一个weisuen.py在spiders文件下
并且已经自动生成了下面代码
class WeisuenSpider(scrapy.Spider):
name = 'weisuen'
allowed_domains = ['iqianyue.com']
start_urls = ['http://iqianyue.com/']
def parse(self, response):
pass
这个名为parse的方法是处理scrapy爬虫爬行到网页响应的默认方法,如果有特别指定的回调函数就不用这个,一般默认这个,由自己编写,
同时该方法也负责链接的跟进
def parse(self, response):
item=HellScrapyItem()
item['urlname']=response.xpath('/html/head/title/text()')
print(item['urlname'])
然后执行scrapy crawl weisuen就会打印出来开头的文字
[<Selector xpath='/html/head/title/text()' data='千月枫痕_遇见更文艺的你_千月枫痕'>]
就OK了
除了parse,Scrapy的Spider中常见的属性和方法还有
make_requests_from_url(url)
该方法被start_requests()调用,实现生成Request请求对象,回调默认的是parse()函数,当然也可以回调指定函数如
def make_requests_from_url(self,url):
return scrapy.Request(url=url,callback=self.parse_index)
def parse_index(self,response):
print('Baidu',response.status)
start_requests()
该方法默认读取start_url属性中定义的网址,并为每个网址生成一个Request请求对象,并返回迭代对象,当然也可以不用start_url中的网址,也可以自定义
如
import scrapy
from hell_scrapy.items import HellScrapyItem
class WeisuenSpider(scrapy.Spider):
name = 'weisuen'
allowed_domains = ['iqianyue.com']
start_urls = ['http://iqianyue.com/']
urls2=(
'http://www.jd.com',
'http://sina.com.cn',
'http://yum.iqianyue.com',
)
def start_requests(self):
for url in self.urls2:
yield self.make_requests_from_url(url)
def parse(self, response):
item=HellScrapyItem()
item['urlname']=response.xpath('/html/head/title/text()')
print(item['urlname'])
这样的话我们的start_urls其实是没有作用的
如输出答案为
[<Selector xpath='/html/head/title/text()' data='京东(JD.COM)-正品低价、品质保障、配送及时、轻松购物!'>]
[<Selector xpath='/html/head/title/text()' data='新浪首页'>]
[<Selector xpath='/html/head/title/text()' data='韬云科技|国内首家企业专属云平台'>]
还可以这样
start_requests(self):
yield scrapy.Request(url='http://www.baidu.com',callback=self.parse.index)
指定url或者一个url列表函数
__init__()
该方法主要负责爬虫的初始化,为构造函数
当然在传参时它的作用也无比明显
allowed_domains是允许的域名,当有很多允许域名而又懒的添加时可以删去。
这些都可以在上面的连接点开教程看
现在让我们看看我们的蜘蛛被修改为递归地跟随到下一页的链接,从中提取数据:
import scrapy
class QuotesSpider(scrapy.Spider):
name = "quotes"
start_urls = [
'http://quotes.toscrape.com/page/1/',
]
def parse(self, response):
for quote in response.css('div.quote'):
yield {
'text': quote.css('span.text::text').get(),
'author': quote.css('small.author::text').get(),
'tags': quote.css('div.tags a.tag::text').getall(),
}
next_page = response.css('li.next a::attr(href)').get()
if next_page is not None:
next_page = response.urljoin(next_page)
yield scrapy.Request(next_page, callback=self.parse)
现在,在提取数据之后,该parse()方法查找到下一页的链接,使用该urljoin()方法构建完整的绝对URL (因为链接可以是相对的)并向下一页生成新请求,将自身注册为回调处理下一页的数据提取并保持爬网遍历所有页面。
你在这里看到的是Scrapy跟踪链接的机制:当你在回调方法中产生一个Request时,Scrapy会安排发送该请求并注册一个回调方法,以便在该请求完成时执行。
使用此功能,您可以根据您定义的规则构建跟踪链接的复杂爬网程序,并根据其访问的页面提取不同类型的数据。
在我们的示例中,它创建了一种循环,跟随到下一页的所有链接,直到它找不到 - 用于爬行博客,论坛和其他具有分页的网站。
创建请求的快捷方式
作为创建Request对象的快捷方式,您可以使用 response.follow:
import scrapy
class QuotesSpider(scrapy.Spider):
name = "quotes"
start_urls = [
'http://quotes.toscrape.com/page/1/',
]
def parse(self, response):
for quote in response.css('div.quote'):
yield {
'text': quote.css('span.text::text').get(),
'author': quote.css('span small::text').get(),
'tags': quote.css('div.tags a.tag::text').getall(),
}
next_page = response.css('li.next a::attr(href)').get()
if next_page is not None:
yield response.follow(next_page, callback=self.parse)
与scrapy.Request不同,它response.follow直接支持相对URL - 无需调用urljoin(urljoin是构造方法前面)。
绝对URL,就是总是以域名(或者/)开头的网址就是"绝对URL"。
注释:"/"代表域名对应的网站根目录。
例如:http://www.baidu.com/image/baobao.gif。注意,response.follow只返回一个Request实例; 你仍然需要提出这个请求。
您也可以传递选择器response.follow而不是字符串; 此选择器应提取必要的属性:
for href in response.css('li.next a::attr(href)'):
yield response.follow(href, callback=self.parse)
对于<a>元素,有一个快捷方式:response.follow自动使用其href属性。所以代码可以进一步缩短:
for a in response.css('li.next a'):
yield response.follow(a, callback=self.parse)
注意
response.follow(response.css('li.next a'))无效
是因为 response.css返回一个类似于列表的对象,其中包含所有结果的选择器,而不是单个选择器。甲for象在上面的例子中循环,或 是好的。response.follow(response.css('li.next a')[0])
更多示例和模式
import scrapy
class AuthorSpider(scrapy.Spider):
name = 'author'
start_urls = ['http://quotes.toscrape.com/']
def parse(self, response):
# follow links to author pages
for href in response.css('.author + a::attr(href)'):
yield response.follow(href, self.parse_author)
# follow pagination links
for href in response.css('li.next a::attr(href)'):
yield response.follow(href, self.parse)
def parse_author(self, response):
def extract_with_css(query):
return response.css(query).get(default='').strip()
yield {
'name': extract_with_css('h3.author-title::text'),
'birthdate': extract_with_css('.author-born-date::text'),
'bio': extract_with_css('.author-description::text'),
}
这个蜘蛛将从主页面开始,它将跟随作者页面的所有链接,parse_author为每个页面调用回调,以及parse我们之前看到的与回调的分页链接。这里我们将回调传递给response.follow位置参数以使代码更短; 它也适用于scrapy.Request。
该parse_author回调定义了一个辅助函数从CSS查询提取和清理数据,并产生了Python字典与作者的数据。
这个蜘蛛演示的另一个有趣的事情是,即使同一作者有很多引用,我们也不必担心多次访问同一个作者页面。默认情况下,Scrapy会筛选出已访问过的URL的重复请求,从而避免因编程错误而导致服务器过多的问题。这可以通过设置进行配置 DUPEFILTER_CLASS
这个简而言之就是
开始 进入第一个for循环一直将页面里的作者信息链接提取出来,通过follow方法生成Request再回调parser_author函数这样一直到这一页的作者链接完毕,然后下一个for循环 即下一页。
就如前面所言。当你在回调方法中产生一个Request时,Scrapy会安排发送该请求并注册一个回调方法,以便在该请求完成时执行。
传参
import scrapy
class QuotesSpider(scrapy.Spider):
name = "quotes"
def start_requests(self):
url = 'http://quotes.toscrape.com/'
tag = getattr(self, 'tag', None)
if tag is not None:
url = url + 'tag/' + tag
yield scrapy.Request(url, self.parse)
def parse(self, response):
for quote in response.css('div.quote'):
yield {
'text': quote.css('span.text::text').get(),
'author': quote.css('small.author::text').get(),
}
next_page = response.css('li.next a::attr(href)').get()
if next_page is not None:
yield response.follow(next_page, self.parse)
如果您将tag=humor参数传递给此蜘蛛,您会注意到它只会访问humor标记中的URL ,例如 http://quotes.toscrape.com/tag/humor。
蜘蛛可以在__init__方法中访问参数:
import scrapy
class MySpider(scrapy.Spider):
name = 'myspider'
def __init__(self, category=None, *args, **kwargs):
super(MySpider, self).__init__(*args, **kwargs)
self.start_urls = ['http://www.example.com/categories/%s' % category]
# ...
通用蜘蛛
Scrapy附带了一些有用的通用蜘蛛,您可以使用这些蜘蛛来对您的蜘蛛进行子类化。他们的目的是为一些常见的抓取案例提供方便的功能,例如根据特定规则跟踪站点上的所有链接,从站点地图抓取或解析XML / CSV Feed。
对于以下蜘蛛中使用的示例,我们假设您有一个TestItem在myproject.items模块中声明的项目:
import scrapy
class TestItem(scrapy.Item):
id = scrapy.Field()
name = scrapy.Field()
description = scrapy.Field()
抓取蜘蛛
类scrapy.spiders.CrawlSpider
这是用于抓取常规网站的最常用的蜘蛛,因为它通过定义一组规则为跟踪链接提供了便利的机制。它可能不是最适合您的特定网站或项目,但它在几种情况下足够通用,因此您可以从它开始并根据需要覆盖它以获得更多自定义功能,或者只是实现您自己的蜘蛛。
除了从Spider继承的属性(您必须指定)之外,此类还支持一个新属性:
rules
这是一个(或多个)Rule对象的列表。每个都Rule 定义了爬网站点的特定行为。规则对象如下所述。如果多个规则匹配相同的链接,则将根据它们在此属性中定义的顺序使用第一个规则。
这个蜘蛛还暴露了一个可重写的方法:
parse_start_url(回应)
为start_urls响应调用此方法。它允许解析初始响应,并且必须返回 Item对象,Request 对象或包含其中任何一个的iterable。
爬行规则
class scrapy.spiders.Rule(link_extractor,callback = None,cb_kwargs = None,follow = None,process_links = None,process_request = None )
link_extractor是一个Link Extractor对象,它定义如何从每个已爬网页面中提取链接。
callback是一个可调用的或一个字符串(在这种情况下,将使用来自具有该名称的spider对象的方法)为使用指定的link_extractor提取的每个链接调用。此回调接收响应作为其第一个参数,并且必须返回包含Item和/或 Request对象(或其任何子类)的列表。
CrawlSpider示例
现在让我们看看带有规则的示例CrawlSpider:
import scrapy
from scrapy.spiders import CrawlSpider, Rule
from scrapy.linkextractors import LinkExtractor
class MySpider(CrawlSpider):
name = 'example.com'
allowed_domains = ['example.com']
start_urls = ['http://www.example.com']
rules = (
# Extract links matching 'category.php' (but not matching 'subsection.php')
# and follow links from them (since no callback means follow=True by default).
Rule(LinkExtractor(allow=('category\.php', ), deny=('subsection\.php', ))),
# Extract links matching 'item.php' and parse them with the spider's method parse_item
Rule(LinkExtractor(allow=('item\.php', )), callback='parse_item'),
)
def parse_item(self, response):
self.logger.info('Hi, this is an item page! %s', response.url)
item = scrapy.Item()
item['id'] = response.xpath('//td[@id="item_id"]/text()').re(r'ID: (\d+)')
item['name'] = response.xpath('//td[@id="item_name"]/text()').get()
item['description'] = response.xpath('//td[@id="item_description"]/text()').get()
return item
这个蜘蛛会开始抓取example.com的主页,收集类别链接和项链接,使用该parse_item方法解析后者。对于每个项目响应,将使用XPath从HTML中提取一些数据,并将Item使用它填充。
XMLFeedSpider
而怎样创建呢
scrapy genspider -l
就能看到一系列
再通过scrapy genspider -t xmlfeed [name] [域名]
j就可以了
而且xmlfeedspider自带parse_node就像自带的普通爬虫项目的parse一样
类scrapy.spiders.XMLFeedSpider
XMLFeedSpider用于通过按某个节点名称迭代XML feed来解析XML feed。迭代器可以选自:iternodes,xml,和html。iternodes出于性能原因,建议使用迭代器,因为xml和html迭代器一次生成整个DOM以便解析它。但是,在使用html错误标记解析XML时,使用迭代器可能很有用。
要设置迭代器和标记名称,必须定义以下类属性:
iterator
一个字符串,它定义要使用的迭代器。它可以是:
'iternodes' - 基于正则表达式的快速迭代器
'html'- 使用的迭代器Selector。请记住,这使用DOM解析,并且必须在内存中加载所有DOM,这可能是大型Feed的问题
'xml'- 使用的迭代器Selector。请记住,这使用DOM解析,并且必须在内存中加载所有DOM,这可能是大型Feed的问题
它默认为:'iternodes'。
itertag
一个字符串,其中包含要迭代的节点(或元素)的名称。示例:
itertag = 'product'
namespaces
一个元组列表,用于定义将使用此spider处理的该文档中可用的名称空间。的 和将被用于自动注册使用的命名空间 的方法。(prefix, uri)prefixuriregister_namespace()
然后,您可以在itertag 属性中指定具有名称空间的节点。
例:
class YourSpider(XMLFeedSpider):
namespaces = [('n', 'http://www.sitemaps.org/schemas/sitemap/0.9')]
itertag = 'n:url'
# ...
除了这些新属性外,此蜘蛛还具有以下可重写方法:
adapt_response(回应)
在蜘蛛开始解析之前,一旦从蜘蛛中间件到达就接收响应的方法。它可以在解析之前用于修改响应主体。此方法接收响应并返回响应(可能是相同或另一个)。
parse_node(响应,选择器)
对于与提供的标记名称(itertag)匹配的节点,调用此方法。接收Selector每个节点的响应和响应 。必须覆盖此方法。否则,你的蜘蛛将无法正常工作。此方法必须返回Item对象, Request对象或包含其中任何对象的iterable。
process_results(回应,结果)
为蜘蛛返回的每个结果(项目或请求)调用此方法,并且它旨在执行将结果返回到框架核心之前所需的任何上次处理,例如设置项目ID。它会收到一个结果列表以及产生这些结果的响应。它必须返回结果列表(项目或请求)。
XMLFeedSpider示例
这些蜘蛛很容易使用,让我们来看一个例子:
class MyxmlspiderSpider(XMLFeedSpider):
name = 'myxmlspider'
allowed_domains = ['sina.com.cn']
start_urls = ['http://blog.sina.com.cn/rss/1615888477.xml']
iterator = 'iternodes' # you can change this; see the docs
itertag = 'rss' # change it accordingly
def parse_node(self, response, node):
# item = {}
i=MyxmlItem()#在item里面定义好了的
#item['url'] = selector.select('url').get()
#item['name'] = selector.select('name').get()
#item['description'] = selector.select('description').get()
i['title']=node.xpath('/rss/channel/item/title/text()').extract()
i['link']=node.xpath('rss/channel/item/link/text()').extract()
i['author']=node.xpath('rss/channel/item/author/text()').extract()
for j in range(len(i['title'])):
print('第'+str(j+1)+'篇文章')
print('标题是:')
print(i['title'][j])
print('对应的链接是:')
print(i['link'][j])
print('对应的作者是:')
print(i['author'][j])
print('===============')
return i
from scrapy.spiders import XMLFeedSpider
from myproject.items import TestItem
class MySpider(XMLFeedSpider):
name = 'example.com'
allowed_domains = ['example.com']
start_urls = ['http://www.example.com/feed.xml']
iterator = 'iternodes' # This is actually unnecessary, since it's the default value
itertag = 'item'
def parse_node(self, response, node):
self.logger.info('Hi, this is a <%s> node!: %s', self.itertag, ''.join(node.getall()))
item = TestItem()
item['id'] = node.xpath('@id').get()
item['name'] = node.xpath('name').get()
item['description'] = node.xpath('description').get()
return item
基本上我们在那里做的是创建一个蜘蛛,从给定的下载源start_urls,然后遍历每个item标签,打印出来,并存储一些随机数据Item。
CSVFeedSpider
类scrapy.spiders.CSVFeedSpider
这个蜘蛛与XMLFeedSpider非常相似,只不过它遍历行而不是节点。在每次迭代中调用的方法是parse_row()。
delimiter
CSV文件中每个字段的分隔符字符串默认为','(逗号)。
quotechar
带有CSV文件中每个字段的机箱字符的字符串默认为'"'(引号)。
headers
CSV文件中的列名列表。
parse_row(响应,行)
使用CSV文件的每个提供(或检测到的)标头的密钥接收响应和dict(表示每行)。该蜘蛛还提供了覆盖adapt_response和process_results用于预处理和后处理目的的方法的机会。
CSVFeedSpider示例
scrapy genspider -t csvfeed mycsvspider iqianyue.com
让我们看一个与前一个类似的示例,但使用 CSVFeedSpider:
from scrapy.spiders import CSVFeedSpider
from myproject.items import TestItem
class MySpider(CSVFeedSpider):
name = 'example.com'
allowed_domains = ['example.com']
start_urls = ['http://www.example.com/feed.csv']
delimiter = ';'
quotechar = "'"
headers = ['id', 'name', 'description']
def parse_row(self, response, row):
self.logger.info('Hi, this is a row!: %r', row)
item = TestItem()
item['id'] = row['id']
item['name'] = row['name']
item['description'] = row['description']
return item
在项目之间共享根目录
项目根目录(包含该目录的目录)scrapy.cfg可以由多个Scrapy项目共享,每个项目都有自己的设置模块。
在这种情况下,你必须定义下这些设置模块的一个或多个别名[settings]在您的scrapy.cfg文件:
[settings]
default = myproject1.settings
project1 = myproject1.settings
project2 = myproject2.settings
默认情况下,scrapy命令行工具将使用这些default设置。使用SCRAPY_PROJECT环境变量指定scrapy要使用的其他项目:
$ scrapy settings --get BOT_NAME
Project 1 Bot
$ export SCRAPY_PROJECT=project2
$ scrapy settings --get BOT_NAME
Project 2 Bot
项目管道
点击这里还可以去笔记o
这个主要参看这个笔记 ,里面有源码
在一个项目被蜘蛛抓取后,它被发送到项目管道,该项目管道通过顺序执行的几个组件处理它。
每个项目管道组件(有时简称为“项目管道”)是一个实现简单方法的Python类。他们收到一个项目并对其执行操作,同时决定该项目是否应继续通过管道或被丢弃并且不再处理。
项目管道的典型用途是:
清理HTML数据
验证已删除的数据(检查项目是否包含某些字段)
检查重复项(并删除它们)
将已删除的项目存储在数据库中
process_item(self, item, spider)
处理item
open_spider(self, spider)
打开蜘蛛时会调用此方法。
如
from scrapy.exceptions import DropItem
import pymongo
class TextPipeline(object):
def __init__(self):
self.limit=50
def process_item(self, item, spider):
if item['text']:
if len(item['text'])>self.limit:
item['text']=item['text'][0:self.limit].rstrip()+'........' #rstrip去除空格
return item
else:
return DropItem('miss Text')
#官网里有这些代码
class MongoPipeline(object):
def __init__(self,mongo_url,mongo_db):
self.mongo_url=mongo_url
self.mongo_db=mongo_db
@classmethod
def from_crawler(cls,crawler):
return cls(
mongo_url=crawler.settings.get('MONGO_URL'),
mongo_db=crawler.settings.get('MONGO_DB')
)
def open_spider(self,spider):
self.client=pymongo.MongoClient(self.mongo_url)
self.db=self.client[self.mongo_db]
def process_item(self,item,spider):
name=item.__class__.__name__
self.db[name].insert(dict(item))
return item
def close_spider(self,spider):
self.client.close()
并设置好setting
ITEM_PIPELINES = {
'quote.pipelines.TextPipeline': 300,
'quote.pipelines.MongoPipeline': 400
}
spiders下面与init同级添加一个yuotes.py
import scrapy
from quote.items import QuoteItem
class QuotesSpider(scrapy.Spider):
name = 'quotes'
allowed_domains = ['quotes.toscrape.com']
start_urls = ['http://quotes.toscrape.com/']
def parse(self, response):
quotes=response.css('.quote') #.quote是pyquery的特点
for quote in quotes:
item=QuoteItem()
text=quote.css('.text::text').extract_first()
author=quote.css('.author::text').extract_first()
tags=quote.css('.tags .tag::text').extract() #所有结果,多个内容,类似于findall
item['text'] = text
item['author']=author
item['tags']=tags
yield item
next =response.css('.pager .next a::attr(href)').extract_first() #这个可以参看f12里面的 是两个class
url=response.urljoin(next) #可以生成一个完整的url因为是不完整的比如少http://等
yield scrapy.Request(url=url,callback=self.parse) #递归,自己回调自己
items里
class QuoteItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
text=scrapy.Field()
author=scrapy.Field()
tags=scrapy.Field()
download middlewer
点击这里[downloader主页](https://docs.scrapy.org/en/latest/topics/downloader-middleware.html "崔庆才")o
download middlewer
点击这里[downloader崔庆才视频](https://www.bilibili.com/video/av19057145/?p=28 "崔庆才")o
Scrapy批量运行爬虫文件的两种方法:
1、使用CrawProcess实现
https://doc.scrapy.org/en/latest/topics/practices.html
2、修改craw源码+自定义命令的方式实现
crawl命令的源码文件在scrapy官方的github项目中找到(地址是:https://github.com/scrapy/scrapy/blob/master/scrapy/commands/crawl.py
(1)我们打开scrapy.commands.crawl.py 文件可以看到:
def run(self, args, opts):
if len(args) < 1:
raise UsageError()
elif len(args) > 1:
raise UsageError("running 'scrapy crawl' with more than one spider is no longer supported")
spname = args[0]
self.crawler_process.crawl(spname, **opts.spargs)
self.crawler_process.start()
这是crawl.py 文件中的run() 方法,在此可以指定运行哪个爬虫,要运行所有的爬虫,则需要更改这个方法。
run() 方法中通过crawler_process.crawl(spname, **opts.spargs) 实现了爬虫文件的运行,spname代表爬虫名。要运行多个爬虫文件,首先要获取所有的爬虫文件,可以通过crawler_process.spider_loader.list() 实现。
如何获取所有的爬虫文件,如果要获取所有的爬虫文件,可以通过crawler_process.crawl(spname, **opts.spargs) 实现。
在这里,我们将新文件夹的命名为mycmd,在对应目录下创建该文件夹(位置在spiders目录的同级目录下),如下所示:
cd .\mymultispd\
mkdir mycmd
创建好文件夹再创建一个.py文件
复制下面代码
import os
from scrapy.commands import ScrapyCommand
from scrapy.utils.conf import arglist_to_dict
from scrapy.utils.python import without_none_values
from scrapy.exceptions import UsageError
class Command(ScrapyCommand):
requires_project = True
def syntax(self):
return "[options] <spider>"
def short_desc(self):
return "Run a spider"
def add_options(self, parser):
ScrapyCommand.add_options(self, parser)
parser.add_option("-a", dest="spargs", action="append", default=[], metavar="NAME=VALUE",
help="set spider argument (may be repeated)")
parser.add_option("-o", "--output", metavar="FILE",
help="dump scraped items into FILE (use - for stdout)")
parser.add_option("-t", "--output-format", metavar="FORMAT",
help="format to use for dumping items with -o")
def process_options(self, args, opts):
ScrapyCommand.process_options(self, args, opts)
try:
opts.spargs = arglist_to_dict(opts.spargs)
except ValueError:
raise UsageError("Invalid -a value, use -a NAME=VALUE", print_help=False)
if opts.output:
if opts.output == '-':
self.settings.set('FEED_URI', 'stdout:', priority='cmdline')
else:
self.settings.set('FEED_URI', opts.output, priority='cmdline')
feed_exporters = without_none_values(
self.settings.getwithbase('FEED_EXPORTERS'))
valid_output_formats = feed_exporters.keys()
if not opts.output_format:
opts.output_format = os.path.splitext(opts.output)[1].replace(".", "")
if opts.output_format not in valid_output_formats:
raise UsageError("Unrecognized output format '%s', set one"
" using the '-t' switch or as a file extension"
" from the supported list %s" % (opts.output_format,
tuple(valid_output_formats)))
self.settings.set('FEED_FORMAT', opts.output_format, priority='cmdline')
def run(self, args, opts):
if len(args) < 1:
raise UsageError()
elif len(args) > 1:
raise UsageError("running 'scrapy crawl' with more than one spider is no longer supported")
spname = args[0]
self.crawler_process.crawl(spname, **opts.spargs)
self.crawler_process.start()
再修改run
首先,将crawl命令的源码复制到该文件(mycrawl.py)中,然后进行修改:
#主要修改这里
def run(self, args, opts):
#获取爬虫列表
spd_loader_list=self.crawler_process.spider_loader.list()
#遍历各爬虫
for spname in spd_loader_list or args:
self.crawler_process.crawl(spname, **opts.spargs)
print("此时启动的爬虫为:"+spname)
self.crawler_process.start()
然后,可以新建的该源代码文件的同级目录下添加一个初始化文件__init__.py,如下所示:
COMMANDS_MODULE = 'hell_scrapy.mycmd'
并添加进setting
COMMANDS_MODULE = 'hell_scrapy.mycmd'
随后,在命令行进入该项目所在的目录,并输入scrapy -h,出现如下所示的信息:
Available commands:
bench Run quick benchmark test
check Check spider contracts
cmd Run all spider
crawl Run a spider
edit Edit spider
fetch Fetch a URL using the Scrapy downloader
genspider Generate new spider using pre-defined templates
list List available spiders
parse Parse URL (using its spider) and print the results
runspider Run a self-contained spider (without creating a project)
settings Get settings values
shell Interactive scraping console
startproject Create new project
version Print Scrapy version
view Open URL in browser, as seen by Scrapy
发现有cmd 就是自己创建的.py文件,说明OK拉
再执行scrapy cmd --nolog
结果为
此时启动的爬虫为:myxmlspider
此时启动的爬虫为:weisuen
第1篇文章
标题是:
精通Python网络爬虫-新书介绍
对应的链接是:
[<Selector xpath='/html/head/title/text()' data='京东(JD.COM)-正品低价、品质保障、配送及时、轻松购物!'>]
[<Selector xpath='/html/head/title/text()' data='新浪首页'>]
1.禁止cookie
有的网页会通过用户的cookie信息进行识别和分析,此时我们可以通过禁用本地cookie从而让对方网站无法识别
如何禁止呢
在对应的scrapy项目的setting 中
打开发现有这两行代码
# Disable cookies (enabled by default)
#COOKIES_ENABLED = False
只需要把
#COOKIES_ENABLED = False
注释取消就ok拉
# Disable cookies (enabled by default)
COOKIES_ENABLED = False
2.设置下载延时
有的网站通过访问的频率分析的
同样这样的设置在setting 里
# Configure a delay for requests for the same website (default: 0)
# See https://doc.scrapy.org/en/latest/topics/settings.html#download-delay
# See also autothrottle settings and docs
#DOWNLOAD_DELAY = 3
只需要把
DOWNLOAD_DELAY = 3
注释取消就Ok 拉,时间还可以自己设置
3就是三秒
3.使用ip代理池
如果同一个ip在短时间对服务器网页进行爬取,就会被禁止
所以 我们可以为scrapy项目建立一个下载中间件,在setting中配置好下载中间件并配置ip池
首先我们创建一个.py文件,由于我们需要大量的ip 所以可以在
http://yum.iqianyue.com/proxy里去找
找到这些ip后,我们在setting里面添加如下
#ip池设置
IPPOOL=[
{'ipaddr':'121.33.24.2...'},
{'ipaddr':'121.33.24.2...'},
{'ipaddr':'121.33.24.2...'},
{'ipaddr':'121.33.24.2...'},
{'ipaddr':'121.33.24.2...'},
]
此时,IPPOOL就是对应的代理服务器的ip池
设置好ip池后,我们需要编写下载中间件文件,也就是我们先前创建的.py文件
在scrapy中,与代理服务器设置相关的下载中间件事HttpProxyMiddleware
所以详细 参看官方文档
import random
from hell_scrapy.setting import IPPOOL
from scrapy.contrb.downloadermiddleware.httpproxy import HttpProxyMiddleware
class IPPOOLS(HttpProxyMiddleware):
def __init__(self,ip=''):
self.ip=ip
def process_request(self,request,spider):
this_ip=random.choice(IPPOOL)
print('当前选择的ip是:'+this_ip['ipaddr])
request.meta['proxy']='http://'+this_ip['ipaddr']
编写好后,由于这只是一个普通middle.py文件,所以我们需要在setting中配置下
#DOWNLOADER_MIDDLEWARES = {
# 'hell_scrapy.middlewares.HellScrapyDownloaderMiddleware': 543,
#}
更改为
DOWNLOADER_MIDDLEWARES = {
# 'hell_scrapy.middlewares.HellScrapyDownloaderMiddleware': 543,
'scrapy.contrb.downloadermiddleware.httpproxy.HttpProxyMiddleware':123 #根据官方文档配置这个
'hell_scrapy.middle.IPPOOLS':125
}
配置好了后运行scrapy crawl weisuen --nolog ok 拉
创建项目scrapy startproject name
item编写
这里是我们想要的信息
class AutopjtItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
name=scrapy.Field()
price=scrapy.Field()
link=scrapy.Field()
comnum=scrapy.Field()
pipeline编写
这里是存蓄
import json
import codecs
class AutopjtPipeline(object):
def __init__(self):
self.file=codes.open('D:\Python_code\scrapy_env\autopjt\mydata','wb',encoding='utf-8')
def process_item(self, item, spider): #听名字就知道这是处理item的函数,当然返回的也是处理后的item
i=json.dumps(dict(item),ensure_ascii=False)
line=i+'\n'
self.file.write(line)
return item
def close_spider(self,spider):
self.file.close()
srtting 编写
打开item_pipelines
打开cookie
将robotstxt设置fasle
这时出现一点小错误,没有库
解决是打开setting找到虚拟环境,用已存在的虚拟scrapy_env就OK拉
自动爬虫的编写
scrapy genspider -t basic autospd dangdang.com 依据的模板是basic不是xmlfeed等其他
import scrapy
from autopjt.items import AutopjtItem
from scrapy.http import Request
class AutospdSpider(scrapy.Spider):
name = 'autospd'
allowed_domains = ['dangdang.com']
start_urls = ['http://search.dangdang.com/?key=%BB%A8%C9%FA%D3%CD&act=input&page_index=1']
def parse(self, response):
item=AutopjtItem()
#
item['name']=response.xpath("//a[@class='pic']/@title").extract()
item['price']=response.xpath("//span[@class='price_n']/text()").extract()
item['link']=response.xpath("//a[@class='pic']/@href").extract()
item['comnum']=response.xpath("//a[@name='P_p1']/text()").extract()
yield item
for i in range(1.5):
url='http://search.dangdang.com/?key=%BB%A8%C9%FA%D3%CD&act=input&page_index='+str(i)
yield Request(url,callback=self.parse)
这是源码
<a title=" 美临 小榨花生油 5L 浓香花生油" ddclick="act=normalResult_picture&pos=1352623776_0_1_q" class="pic" name="itemlist-picture" dd_name="单品图片" href="http://product.dangdang.com/1352623776.html" target="_blank"><img src="http://img3m6.ddimg.cn/42/13/1352623776-1_b_1.jpg" alt=" 美临 小榨花生油 5L 浓香花生油"><p class="cool_label"></p></a>
<p class="price"> <span class="price_n">¥128.00</span></p>
<p class="name" name="title"><a title=" 美临 小榨花生油 5L 浓香花生油" href="http://product.dangdang.com/1352623776.html" name="itemlist-title" dd_name="单品标题" ddclick="act=normalResult_title&pos=1352623776_0_1_q" target="_blank"> 美临 小榨<font class="skcolor_ljg">花生油</font> 5L 浓香<font class="skcolor_ljg">花生油</font></a></p>
<p class="link"><a href="http://shop.dangdang.com/21079" name="itemlist-shop-name" dd_name="单品店铺" target="_blank" title="美临旗舰店">美临旗舰店</a></p>
<p class="star"><span class="level"><span style="width: 100%;"></span></span><a href="http://product.dangdang.com/1352623776.html?point=comment_point" target="_blank" name="itemlist-review" dd_name="单品评论" ddclick="act=click_review_count&pos=1352623776_0_1_q">333条评论</a></p>
item
name=scrapy.Field()
link=scrapy.Field()
pipeline
class CrawlPipeline(object):
def process_item(self, item, spider):
print(item['name'])
print(item['link'])
print('--------------')
return item
scrapy genspider -l查看
有crawlspider
然后scrapy gendpider -t crawl name sohu.com
from crawl.items import CrawlItem
class CrawlspiderSpider(CrawlSpider):
name = 'crawlspider'
allowed_domains = ['sohu.com']
start_urls = ['http://www.sohu.com/']
# rules = (
# Rule(LinkExtractor(allow=r'Items/'), callback='parse_item', follow=True),
# )
rules = (
Rule(LinkExtractor(allow=('.*?/n.*?shtml'), allowed_domains=('sohu.com')), follow=True),
)
def parse_item(self, response):
i=CrawlItem()
i['name']=response.xpath("/html/head/title/text()").extract()
i['link']=response.xpath("//link[@rel='canonical']/@href").extract()
#item['domain_id'] = response.xpath('//input[@id="sid"]/@value').get()
#item['name'] = response.xpath('//div[@id="name"]').get()
#item['description'] = response.xpath('//div[@id="description"]').get()
return i