scrapy是python最有名的爬虫框架之一,可以很方便的进行web抓取,并且提供了很强的定制型。
一、安装scrapy
# pip install scrapy
二、基本使用
1、初始化scrapy项目
# scrapy startproject myscrapy
初始化完成后的目录结构
# tree
.
├── myscrapy
│ ├── __init__.py
│ ├── items.py # 设置数据存储模板,用于结构化数据
│ ├── middlewares.py # 中间件,相当于钩子,对爬去前后做预处理
│ ├── pipelines.py # 管道模块,处理spider模块分析好的数据,如保存入库等
│ ├── settings.py # 配置文件,如并发数,是否开启缓存
│ └── spiders
│ ├── __init__.py
│ └── myspider.py
└── scrapy.cfg # 项目的配置文件
一个例子爬取当当网的首页
#!/usr/bin/python
#coding:utf-8
import scrapy
class DangDang(scrapy.Spider):
# 必须定义
name = "dangdang"
# 初始urls
start_urls = [
"http://www.dangdang.com"
]
# 默认response处理函数
def parse(self, response): # 抓取start_urls页面,自动执行parse回调函数
current_url = response.url # 当前请求的URL
body = response.body # 请求的内容
print '请求的URL: {}\n请求的内容: {}'.format(current_url,body)
运行
# scrapy crawl dangdang # 方式1
# scrapy runspider dangdang.py # 方式2
爬虫开始爬取start_urls定义的url,并输出到文件中,最后输出爬去报告,会输出爬取得统计结果
2、通过代码运行爬虫
每次进入控制台运行爬虫还是比较麻烦的,而且不好调试,我们可以通过CrawlerProcess通过代码运行爬虫,新建一个模块run.py
#!/usr/bin/python
#coding:utf-8
from scrapy.crawler import CrawlerProcess
from scrapy.utils.project import get_project_settings
from spiders.dangdang import DangDang
# 获取setting.py模块的设置
settings = get_project_settings()
process = CrawlerProcess(settings=settings)
# 添加spider,可以多个
process.crawl(DangDang)
# 启动爬虫,阻塞知道爬取结束
process.start()
只需要执行python run.py就可以执行爬虫
三、Scrapy类
如上面的DangDang类,爬虫类继承自scrapy.Spider
1、常用属性
name
:爬虫的名字,必须唯一(如果在控制台使用的话,必须配置)start_urls
:爬虫初始爬取的链接列表parse
:response结果处理函数custom_settings
:自定义配置,覆盖settings.py
中的默认配置
2、常用方法
start_requests
:启动爬虫的时候调用,默认是调用make_requests_from_url
方法爬取start_urls
的链接,可以在这个方法里面定制,如果重写了该方法,start_urls默认将不会被使用,可以在这个方法里面定制一些自定义的url,如登录,从数据库读取url等,本方法返回Request对象make_requests_from_url
:默认由start_requests
调用,可以配置Request对象,返回Request对象parse
:response到达spider的时候默认调用,如果在Request对象配置了callback函数,则不会调用,parse方法可以迭代返回Item
或Request
对象,如果返回Request对象,则会进行增量爬取
3、Request与Response对象
每个请求都是一个Request对象,Request对象定义了请求的相关信息(url, method, headers, body, cookie, priority)和回调的相关信息(meta, callback, dont_filter, errback),通常由spider迭代返回
其中meta相当于附加变量,可以在请求完成后通过response.meta访问请求完成后,会通过Response对象发送给spider处理,常用属性有(url, status, headers, body, request, meta)
四、选择器
基本的选择器
正则选择
1、基本的选择器
// ## 子孙
/ # 孩子
//div[@class='info'][@id='1'] # 属性选择器
//div/img/@src # div下所有img的src值
//div/a[1] # div下第一个<a>
//div/a[1]/text() # div下第一个<a>的值
例如
想要获取书名,可以写为: //ul[@class='list_aa listimg']/li//a/@title
查找方式
# 方法1,即将被废弃的
from scrapy.selector import HtmlXPathSelector
hxs = HtmlXPathSelector(response)
items_HtmlXPathSelector = hxs.select('//ul[@class='list_aa listimg']/li//a/@title')
print(len(items_HtmlXPathSelector))
# 方法2
from scrapy.selector import Selector
items_Selector = Selector(response=response).xpath('//ul[@class='list_aa listimg']/li//a/@title')
print(len(items_Selector))
2、正则选择器
<body>
<li class="item-"><a href="link.html">first item</a></li>
<li class="item-0"><a href="link1.html">first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
</body>
书写方法://li[re:test(@class, "item-\d*")]//@href
查找方式
ret = Selector(response=response).xpath('//li[re:test(@class, "item-\d*")]//@href').extract()
# re ---- 通过正则进行匹配
# test -- 匹配
五、使用scrapy爬取网站聊天记录和用户头像
#!/usr/bin/python
#coding:utf-8
import scrapy
from scrapy.selector import Selector
import os
import requests
class NextSpider(scrapy.spiders.Spider):
name = 'nextspider'
start_urls = ["http://group.jobbole.com/27740/#comm-77724"]
def parse(self,response):
items_selector = Selector(response=response)
items = items_selector.xpath('//ul[@class="cmnt-list"]/li')
# print items
for i in range(len(items)):
srcs = items_selector.xpath('//ul[@class="cmnt-list"]/li[%d]//div[@class="cmnt-header"]/a/img/@src'%i).extract()
names = items_selector.xpath('//ul[@class="cmnt-list"]/li[%d]//div[@class="cmnt-header"]/div/span[1]/a/text()'%i).extract()
msgs = items_selector.xpath('//ul[@class="cmnt-list"]/li[%d]//div[@class="cmnt-body"]/p/text()'%i).extract()
if srcs and names and msgs:
try:
img_url = srcs[0]
filename = names[0].encode('utf-8')
msg = ','.join([i.encode('utf-8') for i in msgs[1:]])
print '用户ID: {}\n发表信息: {}'.format(filename,msg)
img_dir = 'imgs'
path = os.path.join(os.getcwd(),img_dir,filename+'.png')
r = requests.get(img_url)
with open(path,'wb') as f:
for chunk in r.iter_content(chunk_size=1024):
if chunk:
f.write(chunk)
f.flush()
f.close()
except Exception,e:
print '错误: {}'.format(e)
结果
用户ID: 小编辑
发表信息: 恭喜!,说说你是怎么追到的。然后再打广告 :)
用户ID: 迷路的兔纸-->
发表信息: 你这是赤果果的炫耀
用户ID: sorry丶
发表信息: 10月份去九寨沟,在青旅里面遇到我的那个她。
... ...
图片
# ls imgs/
lishenluo.png sorry丶.png 寻找隐世快乐.png 小编辑.png 忠文.png 熊绎.png 连时光都吐了.png 迷路的兔纸-->.png
六、items.py中的Item类
初始化后默认的Item类
import scrapy
class MyscrapyItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
pass
scrapy.Item的用法与python中的字典用法基本一样,只是做了一些安全限制,属性定义使用Field,这里只是进行了声明,而不是真正的属性,使用的时候通过键值对操作,不支持属性访问
七、Pipeline
spider负责爬虫的配置,item负责声明结构化数据,而对于数据的处理,在scrapy中使用管道的方式进行处理,只要注册过的管道都可以处理item数据(处理,过滤,保存)
示例:这里定义一个预处理管道PretreatmentPipeline.py,如果item的title为None,则设置为空字符串
class PretreatmentPipeline(object):
def process_item(self, item, spider):
if item['msgs']:
# 不让msgs为空
item['msgs'] = ''
return item
八、setting.py文件
setting.py文件中为我们提供了许多自带的功能,例如缓存、多线程等等,只需要修改配置即可
1、缓存
# 打开缓存
HTTPCACHE_ENABLED = True
# 设置缓存过期时间(单位:秒)
#HTTPCACHE_EXPIRATION_SECS = 0
# 缓存路径(默认为:.scrapy/httpcache)
HTTPCACHE_DIR = 'httpcache'
# 忽略的状态码
HTTPCACHE_IGNORE_HTTP_CODES = []
# 缓存模式(文件缓存)
HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'
2、多线程
# 默认Request并发数:32
CONCURRENT_REQUESTS = 32
# 默认每个域名的并发数:16
CONCURRENT_REQUESTS_PER_DOMAIN = 16
# 每个IP的最大并发数:0表示忽略
CONCURRENT_REQUESTS_PER_IP = 16