三十、python scrapy 的概念、安装、使用和案例
Scrapy介绍
Scrapy,Python开发的一个快速,高层次的屏幕抓取和web抓取框架,用于抓取web站点并从页面中提取结构化的数据。Scrapy用途广泛,可以用于数据挖掘、监测和自动化测试。
Scrapy安装
安装前所需安装:https://pypi.python.org 在官网汇中找
Lxml
Zope.interface
Twisted
Pywin32 -exe安装
pyOpenSSL -whl安装
安装完上面的就尅安装:
Scrapy - pip install scrapy(用pip直接连上网安装即可;如用package方式安装得下载包)
安装好后验证一下:
Import scrapy 或者直接在命令窗口输入:scrapy
Scrapy的使用方式
1、交互式
在命令端输入 scrapy shell ‘url’
C:\Users\lyd>scrapy
C:\Users\lyd>scrapy shell "https://www.baidu.com"
运行上面的shell出错:
import win32api
ImportError: No module named win32api
解决方法:
安装:pywin32-221.win-amd64-py2.7.exe 注意64位系统不要安装...win-32-py2.7.exe
>>> response
<200 https://www.baidu.com>
>>> response.xpath('//div')
Response对象:
-xpath() 用xpath提取。主要讲这个
-css() 用css选择器提取
-extract()
-re() 用正则提取感兴趣内容
>>> exit() 退出
2、创建项目
Scrapy startproject projectname
E:\PycharmProjects\WebCrawler>scrapy startproject scrapy01 创建项目
3、创建splider 。本质就是在工程的spider包中创建一个py文件,手动和命令创建都可以。
Scrapy genspider spider ‘starturl’
创建spider:注意starturl可以创建后不对再更正。
E:\PycharmProjects\WebCrawler>cd scrapy01
E:\PycharmProjects\WebCrawler\scrapy01>scrapy genspider DoubanSpider "https://ww
w.douban.com"
E:\PycharmProjects\WebCrawler\scrapy01>scrapy genspider SinaSpider "https://www.sina.com.cn"
代码1:
====================DoubanSpider和SinaSpider类似如下================
class SinaspiderSpider(scrapy.Spider):
name = 'SinaSpider'
allowed_domains = ['www.sina.com.cn']
start_urls = ['http://www.sina.com.cn/'] #可以修改,是一个列表
def parse(self, response):
print response.body
print response.url
=============================================================================
4、运行爬虫
Scrapy crawl spidername
E:\PycharmProjects\WebCrawler\scrapy01>scrapy crawl DoubanSpider
E:\PycharmProjects\WebCrawler\scrapy01>scrapy crawl SinaSpider
5、在pycharm中交互式运行创建的爬虫:
首先在scrapy01/scrapy01包下创建一个main.py文件;其次在里面编写:
fromscrapyimportcmdline
cmdline.execute('scrapy crawl SinaSpider'.split())
然后运行该文件即可;
项目的目录结构:
Scrapy的架构:
各个组件及在系统中发生的数据流的概览(绿色箭头所示)。https://scrapy-chs.readthedocs.io/zh_CN/0.24/topics/architecture.html
各个组件:
Scrapy Engine
引擎负责控制数据流在系统中所有组件中流动,并在相应动作发生时触发事件。详细内容查看下面的数据流(Data Flow)部分。
调度器(Scheduler)
调度器从引擎接受request并将他们入队,以便之后引擎请求他们时提供给引擎。
下载器(Downloader)
下载器负责获取页面数据并提供给引擎,而后提供给spider。
Spiders
Spider是Scrapy用户编写用于分析response并提取item(即获取到的item)或额外跟进的URL的类。 每个spider负责处理一个特定(或一些)网站。 更多内容请看 Spiders 。
Item Pipeline
Item Pipeline负责处理被spider提取出来的item。典型的处理有清理、 验证及持久化(例如存取到数据库中)。 更多内容查看Item Pipeline。
流程:
1. 引擎打开一个网站(open a domain),找到处理该网站的Spider并向该spider请求第一个要爬取的URL(s)。
2. 引擎从Spider中获取到第一个要爬取的URL并在调度器(Scheduler)以Request调度。
引擎向调度器请求下一个要爬取的URL。
3. 调度器返回下一个要爬取的URL给引擎,引擎将URL通过下载中间件(请求(request)方向)转发给下载器(Downloader)。
4. 一旦页面下载完毕,下载器生成一个该页面的Response,并将其通过下载中间件(返回(response)方向)发送给引擎。
5. 引擎从下载器中接收到Response并通过Spider中间件(输入方向)发送给Spider处理。
6. Spider处理Response并返回爬取到的Item及(跟进的)新的Request给引擎。
7. 引擎将(Spider返回的)爬取到的Item给Item Pipeline,将(Spider返回的)Request给调度器。
(从第二步)重复直到调度器中没有更多地request,引擎关闭该网站。
scrapy项目实战
需求:
1. 爬取豆瓣图书频道top250 (爬虫脚本)
https://book.douban.com/top250
2. 爬取目标 (items.py)
图书名字、出版商、评分、热度
内容简介
精彩评论(课后练习)
3. 持久化 (pipelie.py)
保存到mysql、mongdb数据库
将本项目分两步走:
一、先爬取基本信息,即一级页面,保存到本地的csv中
1、构建项目,在cmd中执行命令如下:E:\PycharmProjects\WebCrawler>scrapy startproject scrapydouban
2、在项目的根:scrapydouban下去创建创建main.py文件并设置以该脚本启动运行:
3、在项目下的settings.xml中添加如下:
#add settings of mine
#使用useragent代理,否则使用如下的默认值,显然是爬虫的agent,容易被阻止
USER_AGENT='Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.76 Mobile Safari/537.36'
#保存文件路径和格式,无需手动创建
FEED_URI=u'file:///E:/pythondata/doubanbooks.csv'
FEED_FORMAT='CSV'
#关闭cookies,也是为阻止反爬虫
COOKIES_ENABLED=False
4、在项目下的items.py中添加如下:
fromscrapyimportItem,Field
class DoubanBookItem(Item):
title = Field()
title2 = Field()
info = Field()
rate = Field()
hot = Field()
5、编写bookspider.py文件:
E:\PycharmProjects\WebCrawler\scrapydouban>scrapy genspider bookspider 'https://book.douban.com/top250'
添加内容如下:
fromscrapy.spidersimportBaseSpider
from scrapy.httpimportRequest
from scrapy.selectorimportSelector
#引入自己的模块很多种情况和方式,跨目录引入可以将项目跟目录(点击右键)标记为Mark Directory As Source Root。注意不同环境也不一样
import scrapydouban.itemsimportDoubanBookItem
class Books(BaseSpider):
name = 'DoubanSpider'#需要和main.py中那个相同
start_urls = ['https://book.douban.com/top250']
defparse(self,response):
item = DoubanBookItem()
selector = Selector(response)
books = selector.xpath('//td[@valign="top" and not(@width)]')
foreachbookinbooks:
title = eachbook.xpath('div[@class="pl2"]/a/text()').extract()[0]
# title = title[0]
title2 = eachbook.xpath('p[@class="quote"]/span/text()').extract()
title2 = title2[0]iflen(title2) >0else''
info = eachbook.xpath('p[@class="pl"]/text()').extract()[0]
rate = eachbook.xpath('div[@class="star clearfix"]/span[@class="rating_nums"]/text()').extract()[0]
hot = eachbook.xpath('div[@class="star clearfix"]/span[@class="pl""]/text()').extract()[0]
#采用字典方式赋值
item['title'] = title
item['title2'] = title2
item['info'] = info
item['rate'] = rate
item['hot'] = hot
yielditem
nextlink = selector.xpath('//span[@class="next"]/a/@href').extract()
ifnextlink:
nextlink = nextlink[0]
yieldRequest(nextlink,callback=self.parse)
二、第二阶段,要抓取二级页面的评论。并将其持久化到mysql和mongodb
1、在settings.xml文件中新增两行,使用上持久化的mysqlpipline和mongodbpipline
#多个时逗号分隔,是一个字典结构。key是包名.模块名.类名 ,值是其对应的优先顺序。范围为1-1000
ITEM_PIPELINES = {
'scrapydouban.mongopipline.MongoDoubanBookPipline':300,
'scrapydouban.mysqlpipline.MysqlDoubanBookPipline':400,
}
# mongo settings
MONGODB_SERVER = '192.168.216.7'
MONNGODB_PORT = 27017
MONNGODB_DB = 'qianfeng'
MONNGODB_COLLECTION = 'scrapy.doubanbook'
#设置爬虫时间间隔
DOWNLOAD_DELAY = 2
RANDOMIZE_DOWNLOAD_DELAY = True
2、将原来的items.py数据结构内容注释,新增数据结构如下:
classDoubanBookDetailItem(Item):
title = Field()
title2 = Field()
info = Field()
rate = Field()
hot = Field()
href = Field() #二级页面的评论的url
intr = Field()#二级页面的剪短评论
reviews = Field()#书的所有评论url的列表
3、在scrapydouban包下创建两个管道文件,一个是mongopipline,另一个是mysqlpipline。代码分别如下:
fromscrapy.confimportsettings
from pymongoimportMongoClient
class MongoDoubanBookPipline(object):
#初始化,获取mongo的连接信息
def__init__(self):
dbserver = settings['MONGODB_SERVER']
dbport = settings['MONNGODB_PORT']
dbname = settings['MONNGODB_DB']
collname = settings['MONNGODB_COLLECTION']
client = MongoClient(dbserver,dbport)
db = client[dbname]
self.col = db[collname]
#根据自带生成的piplines.py我们知道这个方法必须实现
defprocess_item(self,item,spider):
book = dict(item) #转成json
self.col.insert(book)
returnitem
defclose_spider(self,spider):
pass
importMySQLdb
class MysqlDoubanBookPipline(object):
#初始化,获取mysql的连接信息
def__init__(self):
self.conn = MySQLdb.Connect(host='192.168.216.7',port=3306,user='root',passwd='root',db='testdb',charset='utf8')
self.conn.autocommit(True)
self.cursor =self.conn.cursor()
#根据自带生成的piplines.py我们知道这个方法必须实现
defprocess_item(self,item,spider):
sql = 'insert into doubanbook(title,title2,rate,hot,intr,info) values(%s,%s,%s,%s,%s,%s)'
bookinfo = [item['title'],item['title2'],item['rate'],item['hot'],item['intr'],item['info']]
self.cursor.execute(sql,bookinfo)
returnitem
#爬虫结束出发一次
defclose_spider(self,spider):
self.cursor.close()
self.conn.close()
4、在spiders包下面新增一个文件bookspider2.py,内容如下:
fromscrapy.spidersimportBaseSpider
from scrapy.httpimportRequest
from scrapy.selectorimportSelector
import scrapy
#引入自己的模块很多种情况和方式,跨目录引入可以将项目跟目录(点击右键)标记为Mark Directory As Source Root。注意不同环境也不一样
from scrapydouban.itemsimportDoubanBookDetailItem
from urlparseimporturljoin
from scrapy.utils.responseimportget_base_url
class Books(BaseSpider):
name = 'DoubanBookDetailSpider'#需要和main.py中那个相同
start_urls = ['https://book.douban.com/top250']
defparse(self,response):
item= DoubanBookDetailItem()#注意此种方式极容易造成重复,因为回调时对象地址不变
selector = Selector(response)
books = selector.xpath('//td[@valign="top" and not(@width)]')
foreachbookinbooks:
title = eachbook.xpath('div[@class="pl2"]/a/text()').extract()[0].strip()
# title = title[0]
title2 = eachbook.xpath('p[@class="quote"]/span/text()').extract()
title2 = title2[0]iflen(title2) >0else''
info = eachbook.xpath('p[@class="pl"]/text()').extract()[0]
rate = eachbook.xpath('div[@class="star clearfix"]/span[@class="rating_nums"]/text()').extract()[0]
hot = eachbook.xpath('div[@class="star clearfix"]/span[@class="pl"]/text()').extract()[0]
href = eachbook.xpath('div[@class="pl2"]/a/@href').extract()[0]#书的url
#采用字典方式赋值
# item['title'] = title
# item['title2'] = title2
# item['info'] = info
# item['rate'] = rate
# item['hot'] = hot
# item['href'] = href
# item['intr'] = '' #没有数据先给初始值
# item['reviews'] = []
# yield item
print
#如果需要回调时,传过去的对象地址一样,所以会重复
yieldRequest(url=href,callback=self.parse_bookdetail,dont_filter=False,
meta={'item': DoubanBookDetailItem(title=title,title2=title2,info=info,
rate=rate,hot=hot,href=href,intr='',reviews=[])})
nextlink = selector.xpath('//span[@class="next"]/a/@href').extract()
ifnextlink:
nextlink = nextlink[0]
yieldRequest(nextlink,callback=self.parse,dont_filter=False)
defparse_bookdetail(self,response):
selector = Selector(response)
intrp = selector.xpath('//div[@class="related_info"]/div[@class="indent"]/div/div[@class="intro"]/p/text()').extract()
iflen(intrp) >0:
item = response.meta['item']#取出上一个方法存入的数据
intr ='\r\n'.join(intrp)#将intr数组中的每一段都拼接上换行符
item['intr'] = intr.lstrip().rstrip()
yielditem
5、修改main.py中的运行spiderName:
fromscrapyimportcmdline
cmdline.execute('scrapy crawl DoubanBookDetailSpider'.split())
三、第三阶段,将二级页面的评论也爬虫下来。
1、修改bookspider2.py文件,其实是修改一个方法和添加一个方法:
defparse_bookdetail(self,response):
selector = Selector(response)
intrp = selector.xpath('//div[@class="related_info"]/div[@class="indent"]/div/div[@class="intro"]/p/text()').extract()
iflen(intrp) >0:
item = response.meta['item']#取出上一个方法存入的数据
intr ='\r\n'.join(intrp)#将intr数组中的每一段都拼接上换行符
item['intr'] = intr.lstrip().rstrip()
# yield item #第二阶段使用的,如果还要继续抓取评论则需要注释#####
baseurl = get_base_url(response)
#https://book.douban.com/subject/1770782/reviews 评论的url是在书的超链接加reviews
reviewurl = urljoin(baseurl,'reviews')
print'reviewurl=====',reviewurl
yieldRequest(url=reviewurl,meta={'item': item},callback=self.parse_bookreview)
def parse_bookreview(self,response):
item = response.meta['item']
selector = Selector(response)
# reviews = selector.xpath('//div[@class="review-list"]/div[@xmlns:v="http://rdf.data-vocabulary.org/#"]/div[@class="main review-item"]/div[@class="main-bd"]/div[@class="review-short"]/div[@class="short-content"]/text()').extract()
reviews = selector.xpath('//div[@class="short-content"]/text()').extract()
printreviews
ifreviews:
item['reviews'].extend(reviews)
nextlink = selector.xpath('//span[@class="next"]/a/@href').extract()
ifnextlink:
nextlink = nextlink[0]
yieldscrapy.Request(nextlink,callback=self.parse_obokreview,meta={'item':item})
else:
yielditem
然后测试即可。