scrapy简单介绍:
- 基于twisted异步io框架,性能是最大的优势
- 可以加入request和beautifulsoup
- 方便扩展,提供了很多内置功能
- 内置的css和xpath selector非常方便
- 默认深度优先
分析页面
- 国内用百度,国外用Google, 不同的国家收录的页面数量不一样, 访问百度页面,输入site: www.***.com查看收录的页面
- 分析不同页面间的Cookies传递
- 利用sitemap分析网站结构和估算, 可以在/robots.txt中找到
- 查看json数据以及源码和渲染之后的页面,查看提交的方式有没有js函数,还有初始加载的js文件,找到规律规则
Scrapy选择器:
- response.css()
- response.css(“div.quote”)
查找div块class等于quote
- response.css(“div.tags a.tag::text”)
查找div块等于tags并且子元素a标签class也等于tag
- response.css(“div.quote”)
- response.xpath()
- response.re() 返回unicode字符串列表, 使用re函数会有小的性能损失
Scrapy选择器内置方法
css选择器和xpath选择器可以互相嵌套
路径表达式 | 结果 |
---|---|
选择器表达式.extract() | 获得选择器数组 |
选择器表达式.extract_first() | 获取数组里第一个值,没有值默认为None |
re_first() | 提取第一个匹配的字符串 |
//div/@href | 获取所有div下的href属性值(用于xpath选择器) |
//div/text() | 获取所有div下的文本(用于xpath选择器) |
div.quote ::text | 获得class值为quote的div块下的文本(用于css选择器) |
div.quote ::attr(href) | 获得class值为quote的div块下的href属性值(用于css选择器) |
//input[start-with(@id,‘fuck’)] | 匹配id以fuck开头的元素,id=‘fuckyou’ |
//input[ends-with(@id,‘fuck’)] | 匹配id以fuck结尾的元素,id=‘youfuck’ |
//input[contains(@id,‘fuck’)] | 匹配id中含有fuck的元素,id=‘youfuckyou’ |
//div/last() | 选取div块最后一个元素 |
//div[position()< 3] | 选取当前元素下最前面的两个子元素 |
注意//node[1]和(//node)[1]之间的区别
Scrapy带UA调试启动
scrapy shell -s 'User_Agent'="UA" www.baidu.com
页面追踪:
yield response.follow(相对url, callback=self.parse)
对于<a>
元素,有一个简单方式: response.follow会自动使用它们的href属性。因此,代码可以进一步缩短
for a in response.css('li.next a'):
yield response.follow(a, callback=self.parse)
IDE调试爬虫
在spider项目里创建个main文件, 把下面添加进去,
from scrapy.cmdline import execute
execute(["scrapy", "crawl", "spider"])
Windows下定时轮询爬虫
from os import system
system("scrapy crawl spider")
log调试
self.logger.info('调试')
或
self.log('调试')
Spider接收参数
终端传递参数
scrapy crawl spider -a='arg' -b='kwarg'
在Spider里接收参数
import scrapy
class MySpider(scrapy.Spider):
name = 'myspider'
def __init__(self, a=None, b=None, *args, **kwargs):
super(MySpider, self).__init__(*args, **kwargs)
self.a = a
self.b = b
# ...
spider参数只能是字符串
Scrapy shell调试response
from scrapy.shell import inspect_response
def parse_details(self, response):
item = response.meta.get('item', None)
if item:
# 填充更多 `item` 字段
return item
else:
inspect_response(response, self) # scrapy shell调试resposne
浏览器打开源代码网页
from scrapy.utils.response import open_in_browser
def parse_details(self, response):
if "item name" not in response.body:
open_in_browser(response) # 打开源代码网页
单独设置每个spider的settings
class MySpider(scrapy.Spider):
name = 'myspider'
custom_settings = {
'SOME_SETTING': 'some value',
}
访问spider的全局settings
class MySpider(scrapy.Spider):
name = 'myspider'
start_urls = ['http://example.com']
def parse(self, response):
print(u"现有的设置: %s" % self.settings.attributes.keys())
注意!!!
settings
属性是在初始化spider之后在基本spider类中设置的。
如果您想在初始化之前使用这些设置(例如,在您的spider的__init__()
方法中),
您需要覆盖from_crawler()方法。
class MyExtension(object):
def __init__(self, log_is_enabled=False):
if log_is_enabled:
print(u"log已经激活!")
@classmethod
def from_crawler(cls, crawler):
settings = crawler.settings
return cls(settings.getbool('LOG_ENABLED'))
settings对象可以像dict一样使用(例如,settings[‘LOG_ENABLED’]),但是通常首选使用Settings API提供的方法提取设置,以避免类型错误。
Extensions(扩展)例子
import logging
from scrapy import signals
from scrapy.exceptions import NotConfigured
logger = logging.getLogger(__name__)
class SpiderOpenCloseLogging(object):
def __init__(self, item_count):
self.item_count = item_count
self.items_scraped = 0
@classmethod
def from_crawler(cls, crawler):
# 首先检查extension是否应该被启用,否则抛出notconfigure
if not crawler.settings.getbool('MYEXT_ENABLED'):
raise NotConfigured
# 从settings中获取items的数量
item_count = crawler.settings.getint('MYEXT_ITEMCOUNT', 1000)
# 实例化extension对象
ext = cls(item_count)
# 将extension对象连接到signals
crawler.signals.connect(ext.spider_opened, signal=signals.spider_opened)
crawler.signals.connect(ext.spider_closed, signal=signals.spider_closed)
crawler.signals.connect(ext.item_scraped, signal=signals.item_scraped)
# 返回extension对象
return ext
def spider_opened(self, spider):
logger.info(u"打开spider %s", spider.name)
def spider_closed(self, spider):
logger.info(u"关闭spider %s", spider.name)
def item_scraped(self, item, spider):
self.items_scraped += 1
if self.items_scraped % self.item_count == 0:
logger.info(u"已经抓取了 %d items", self.items_scraped)
Signals(信号)
from scrapy import signals
from scrapy import Spider
class DmozSpider(Spider):
name = "dmoz"
allowed_domains = ["dmoz.org"]
start_urls = [
"http://www.dmoz.org/Computers/Programming/Languages/Python/Books/",
"http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/",
]
@classmethod
def from_crawler(cls, crawler, *args, **kwargs):
spider = super(DmozSpider, cls).from_crawler(crawler, *args, **kwargs)
crawler.signals.connect(spider.spider_closed, signal=signals.spider_closed)
return spider
def spider_closed(self, spider):
spider.logger.info('Spider closed: %s', spider.name)
def parse(self, response):
pass
暂停与重启
不同的spider不能共用一个目录(也包括run),暂停中间状态包括之前没有做完的requests,之前过滤器的filter,哪些已经看过了,还有spider状态,提供相关的信息放到这个目录下面
第一次结束的时候以什么目录开始,启动的时候就以什么样继续,就可以继续爬取
开始新的爬虫只要换新的目录就可以
在shell里面启动能发出contortsit命令,在linux里面结束进程kill -f会发出中断的信号可以进行后续的处理, kill -f -9会强制杀掉进程, 无法接收中断的信号, 做不到善后的工作
scrapy crawl spider -s JOBDIR=路径/文件
Request.meta特殊键
键 | 值 |
---|---|
download_timeout | 在超时之前,downloader将等待最大时间(秒) |
download_latency | 用于获取response的时间 |
max_retry_times | 每个request的重试次数, max_retry_times meta键比RETRY_TIMES settings优先级更高。 |
dont_redirect | 默认False , 是否根据response状态处理重定向 |
Scrapy Settings
键 | 值 |
---|---|
LOG_ENABLED | 开启log日志 |
LOG_ENCODING | 同上, 设置log的编码, 默认utf-8 |
LOG_FILE | 设置输出log文件的名称(也可以在命令-s 显式设置文件名), 默认'None' |
LOG_FORMAT | 用于格式化日志消息的字符串, 默认'%(asctime)s [%(name)s] %(levelname)s: %(message)s' |
LOG_DATEFORMAT | 用于格式化日期/时间的字符串,以LOG_FORMAT展开%(asctime)s 的占位符 |
LOG_LEVEL | log的最低级别。默认'DEBUG' , 可用的级别是:CRITICAL (关键级别)、ERROR (错误级别)、WARNING (警告级别)、INFO (信息级别)、DEBUG (调试级别) |
LOG_STDOUT | 默认False , 如果为True ,进程的所有标准输出(和错误)将被重定向到日志。例如,如果您print 'hello' ,它将出现在Scrapy log中。 |
LOG_SHORT_NAMES | 默认False , 如果为True ,日志只包含根路径。如果设置为False ,则显示负责日志输出的组件 |
*********************** | ***************************** |
CONCURRENT_ITEMS | Items并行处理的最大数量, 默认100 |
CONCURRENT_REQUESTS | 并发request的最大数量, 默认16 |
CONCURRENT_REQUESTS_PER_DOMAIN | 对domain执行的最大并发数量, 默认8 |
CONCURRENT_REQUESTS_PER_IP | 限制单个ip最大的并发request数量, 默认0 |
*********************** | ***************************** |
DOWNLOAD_DELAY | 每个request的下载延迟, 默认0 |
DOWNLOAD_TIMEOUT | request最大全局超时时间, 默认180 秒(也可以使用Request.meta键来单独为request设置超时时间) |
*********************** | ***************************** |
MEMDEBUG_ENABLED | 默认'False' , True 启用内存调试 |
*********************** | ****************************** |
COOKIES_ENABLED | 默认True , 保持session会话 |
*********************** | ****************************** |
RETRY_ENABLED | 默认False , 是否启用重试middleware |
RETRY_TIMES | 默认2 , 除了第一次下载外, 重试的最多次数 |
RETRY_HTTP_CODES | 默认: [500, 502, 503, 504, 408] 状态需要重试 |