如何发短信办理,取消北京移动4g商旅套餐和上网套餐

1754人阅读
Python爬虫(13)
1. 数据库安装
Win7+python+amd64安装MySQL参考如下网址
http://blog.csdn.net/wklken/article/details/7253245
2. python数据库操作手册
/python/python-mysql.html
3. python爬虫爬到的信息写入数据库
爬虫写入数据库可参考网页:http://blog.csdn.net/u/article/details/
http://blog.csdn.net/u/article/details/
/rwxwsblog/p/4572367.html
以下转自:/rwxwsblog/p/4572367.html
前面小试了一下scrapy抓取博客园的博客(您可在此查看),但是前面抓取的数据时保存为json格式的文本文件中的。这很显然不满足我们日常的实际应用,接下来看下如何将抓取的内容保存在常见的mysql数据库中吧。
  说明:所有的操作都是在“scrapy爬虫成长日记之创建工程-抽取数据-保存为json格式的数据”的基础上完成,如果您错过了这篇文章可以移步这里查看
  环境:mysql5.1.67-log
  操作步骤:
  1、检查python是否支持mysql
[root@bogon ~]# python
Python 2.7.10 (default, Jun
5 2015, 17:56:24)
[GCC 4.4.4
(Red Hat 4.4.4-13)] on linux2
Type &help&, &copyright&, &credits& or &license& for more information.
&&& import MySQLdb
Traceback (most recent call last):
File &&stdin&&, line 1, in &module&
ImportError: No module named MySQLdb
  如果出现:ImportError: No module named MySQLdb则说明python尚未支持mysql,需要手工安装,请参考步骤2;如果没有报错,请调到步骤3
  2、python安装mysql支持
[root@bogon ~]# pip install mysql-python
Collecting mysql-python
Downloading MySQL-python-1.2.5.zip (108kB)
100% |████████████████████████████████| 110kB 115kB/s
Building wheels for collected packages: mysql-python
Running setup.py bdist_wheel for mysql-python
Stored in directory: /root/.cache/pip/wheels/8c/0d/11/d654cad764b9dd2b9e1b0cd76c22f813c5851a
Successfully built mysql-python
Installing collected packages: mysql-python
Successfully installed mysql-python-1.2.5
  安装完以后再次运行步骤1,检查python是否已经支持mysql
  如果还有问题您可以尝试:LC_ALL=C pip install mysql-python
  如果依然报错:error: Python.h: No such file or directory
  您可以尝试先安装python-devel:
yum install python-devel
  3、创建数据库和表
CREATE DATABASE cnblogsdb DEFAULT CHARACTER SET utf8 COLLATE utf8_general_
CREATE TABLE `cnblogsinfo` (
`linkmd5id` char(32) NOT NULL COMMENT 'url md5编码id',
`title` text COMMENT '标题',
`description` text COMMENT '描述',
`link` text
COMMENT 'url链接',
`listUrl` text
COMMENT '分页url链接',
`updated` datetime DEFAULT NULL
COMMENT '最后更新时间',
PRIMARY KEY (`linkmd5id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
&  注意:
    a)、创建数据库的时候加上DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci,这样才不至于出现乱码。我就因为这个问题折腾了很久。
    b)、数据库表的编码为utf8
  4、设置mysql配置信息
  根据前面的文章()我们可以知道,最终scrapy是通过pipelines.py对抓取的结果进行处理的。很显然要保存到mysql数据库中的话,修改pipelines.py这个文件是在所难免的了。然而在进行mysql操作的时候,我们需要先连上数据库,这时候就设计到数据库连接字符串的问题了。我们可以直接写死在pipelines.py文件中,但是这样又不利于程序的维护,因此我们可以考虑将配置信息写在项目的配置文件settings.py中。
  settings.py中添加如下配置项
# start MySQL database configure setting
MYSQL_HOST = 'localhost'
MYSQL_DBNAME = 'cnblogsdb'
MYSQL_USER = 'root'
MYSQL_PASSWD = 'root'
# end of MySQL database configure setting
&  5、修改pipelines.py
  修改完的结果如下,需要注意的pipelines.py中定义了两个类。JsonWithEncodingCnblogsPipeline是写入json文件用的,而MySQLStoreCnblogsPipeline(需要记住,后面会用到哦!)才是写入数据库用的。
  MySQLStoreCnblogsPipeline类做的主要功能有
    a)、读取数据库配置文件,并生成数据库实例,主要通过类方法from_settings实现,
    b)、如果url不存在则直接写入,如果url存在则更新,通过自定义的方法_do_upinsert实现,
    c)、确保url唯一性的md5函数_get_linkmd5id 。
[root@bogon cnblogs]# more pipelines.py
# -*- coding: utf-8 -*-
# Define your item pipelines here
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: http://doc.scrapy.org/en/latest/topics/item-pipeline.html
from scrapy import signals
import json
import codecs
from twisted.enterprise import adbapi
from datetime import datetime
from hashlib import md5
import MySQLdb
import MySQLdb.cursors
class JsonWithEncodingCnblogsPipeline(object):
def __init__(self):
self.file = codecs.open('cnblogs.json', 'w', encoding='utf-8')
def process_item(self, item, spider):
line = json.dumps(dict(item), ensure_ascii=False) + &\n&
self.file.write(line)
return item
def spider_closed(self, spider):
self.file.close()
class MySQLStoreCnblogsPipeline(object):
def __init__(self, dbpool):
self.dbpool = dbpool
@classmethod
def from_settings(cls, settings):
dbargs = dict(
host=settings['MYSQL_HOST'],
db=settings['MYSQL_DBNAME'],
user=settings['MYSQL_USER'],
passwd=settings['MYSQL_PASSWD'],
charset='utf8',
cursorclass = MySQLdb.cursors.DictCursor,
use_unicode= True,
dbpool = adbapi.ConnectionPool('MySQLdb', **dbargs)
return cls(dbpool)
#pipeline默认调用
def process_item(self, item, spider):
d = self.dbpool.runInteraction(self._do_upinsert, item, spider)
d.addErrback(self._handle_error, item, spider)
d.addBoth(lambda _: item)
#将每行更新或写入数据库中
def _do_upinsert(self, conn, item, spider):
linkmd5id = self._get_linkmd5id(item)
#print linkmd5id
now = datetime.utcnow().replace(microsecond=0).isoformat(' ')
conn.execute(&&&
select 1 from cnblogsinfo where linkmd5id = %s
&&&, (linkmd5id, ))
ret = conn.fetchone()
conn.execute(&&&
update cnblogsinfo set title = %s, description = %s, link = %s, listUrl = %s, updated = %s where linkmd5id = %s
&&&, (item['title'], item['desc'], item['link'], item['listUrl'], now, linkmd5id))
#print &&&
update cnblogsinfo set title = %s, description = %s, link = %s, listUrl = %s, updated = %s where linkmd5id = %s
#&&&, (item['title'], item['desc'], item['link'], item['listUrl'], now, linkmd5id)
conn.execute(&&&
insert into cnblogsinfo(linkmd5id, title, description, link, listUrl, updated)
values(%s, %s, %s, %s, %s, %s)
&&&, (linkmd5id, item['title'], item['desc'], item['link'], item['listUrl'], now))
#print &&&
insert into cnblogsinfo(linkmd5id, title, description, link, listUrl, updated)
values(%s, %s, %s, %s, %s, %s)
#&&&, (linkmd5id, item['title'], item['desc'], item['link'], item['listUrl'], now)
#获取url的md5编码
def _get_linkmd5id(self, item):
#url进行md5处理,为避免重复采集设计
return md5(item['link']).hexdigest()
def _handle_error(self, failue, item, spider):
log.err(failure)
&  6、启用MySQLStoreCnblogsPipeline类,让它工作起来
  修改setting.py配置文件,添加MySQLStoreCnblogsPipeline的支持
ITEM_PIPELINES = {
'cnblogs.pipelines.JsonWithEncodingCnblogsPipeline': 300,
'cnblogs.pipelines.MySQLStoreCnblogsPipeline': 300,
  至此,所有的需要修改的文件都修改好了,下面测试看结果如何。
  7、测试
[root@bogon cnblogs]# scrapy crawl CnblogsSpider
  查看数据库结果:
  至此,scrapy抓取网页内容写入数据库的功能就已经实现了。然而这个爬虫的功能还太弱小了,最基本的文件下载、分布式抓取等都功能都还不具备;同时也试想一下现在很多网站的反爬虫抓取的,万一碰到这样的网站我们要怎么处理呢?接下来的一段时间里我们来逐一解决这些问题吧。随便畅想一下,如果爬虫足够强,内容足够多;我们是不是可以打造一个属于自己的垂直搜索引擎呢?想想就兴奋,尽情YY去吧!!!
  最后源码更新至此:
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:69125次
积分:1734
积分:1734
排名:千里之外
原创:89篇
转载:141篇
(7)(17)(7)(9)(12)(17)(20)(18)(20)(10)(7)(13)(9)(20)(42)云产品存储数据库数据处理网络CDN与加速安全监控与管理移动域名服务游戏服务视频服务图像服务语音服务人工智能(AI)通信大数据基础服务大数据可视化服务应用服务独享高性能物理服务器租赁服务分布式消息队列服务稳定托管的MySQL、SQLServer、MariaDB等关系型数据库兼容Redis和Memcached的分布式存储和缓存服务多节点全网覆盖网络加速服务立体化云产品数据监控,智能化数据分析服务专业域名服务,安全、省心、可信赖数字证书一站式管理,快速接入 HTTPS 安全腾讯质量开放平台,提供一站式测试服务高效图片处理、全面的图片鉴定和识别服务提供丰富API的开放语义分析平台专业智能高效的语音处理服务抵御大流量DDoS攻击,守护企业网络安全腾讯安全专家提供专业技术支持服务防止破解盗版,对抗游戏外挂等一站式移动安全解决方案登录注册防撞库,活动防刷、防薅羊毛高效精准识别文本、图片、视频中的色情、暴恐、广告等恶意内容精准识别借贷欺诈风险、检测支付盗刷隐患、监测钓鱼网站解决方案专属金融云,为您实施高可用的业务容灾架构,随心打造镜像服务运维服务软件服务云合作云生态腾讯云计算文档社区资源与工具售后服务
Scrapy入门
本篇会通过介绍一个简单的项目,走一遍Scrapy抓取流程,通过这个过程,可以对Scrapy对基本用法和原理有大体的了解,作为入门。
在本篇开始之前,假设已经安装成功了Scrapy,如果尚未安装,请参照。
本节要完成的任务有:
创建一个Scrapy项目
创建一个Spider来抓取站点和处理数据
通过命令行将抓取的内容导出
在抓取之前,你必须要先创建一个Scrapy项目,可以直接用scrapy命令生成,命令如下:
scrapy startproject tutorial
在任意文件夹运行都可以,如果提示权限问题,可以加sudo运行。这个命令将会创建一个名字为tutorial的文件夹,文件夹结构如下:
|____scrapy.cfg
# Scrapy部署时的配置文件
|____tutorial
| |______init__.py
| |______pycache__
| |____items.py
| |____middlewares.py
| |____pipelines.py
| |____settings.py
| |____spiders
| | |______init__.py
| | |______pycache__
创建Spider
Spider是由你来定义的Class,Scrapy用它来从网页里抓取内容,并将抓取的结果解析。不过这个Class必须要继承Scrapy提供的Spider类scrapy.Spider,并且你还要定义Spider的名称和起始请求以及怎样处理爬取后的结果的方法。
创建一个Spider也可以用命令生成,比如要生成Quotes这个Spider,可以执行命令。
cd tutorial
scrapy genspider quotes
首先进入到刚才创建的tutorial文件夹,然后执行genspider这个命令,第一个参数是Spider的名称,第二个参数是网站域名。执行完毕之后,你会发现在spiders文件夹中多了一个quotes.py,这就是你刚刚创建的Spider,内容如下:
import scrapy
class QuotesSpider(scrapy.Spider):
name = "quotes"
allowed_domains = [""]
start_urls = ['/']
def parse(self, response):
可以看到有三个属性,name,allowed_domains,start_urls,另外还有一个方法parse
name,每个项目里名字是唯一的,用来区分不同的Spider。
allowed_domains允许爬取的域名,如果初始或后续的请求链接不是这个域名下的,就会被过滤掉。
start_urls,包含了Spider在启动时爬取的url列表,初始请求是由它来定义的。
parse,是Spider的一个方法,默认情况下,被调用时start_urls里面的链接构成的请求完成下载后,返回的response就会作为唯一的参数传递给这个函数,该方法负责解析返回的response,提取数据或者进一步生成要处理的请求。
Item是保存爬取数据的容器,它的使用方法和字典类似,虽然你可以用字典来表示,不过Item相比字典多了额外的保护机制,可以避免拼写错误或者为定义字段错误。
创建Item需要继承scrapy.Item类,并且定义类型为scrapy.Field的类属性来定义一个Item。观察目标网站,我们可以获取到到内容有text, author, tags
所以可以定义如下的Item,修改items.py如下:
import scrapy
class QuoteItem(scrapy.Item):
text = scrapy.Field()
author = scrapy.Field()
tags = scrapy.Field()
定义了三个Field,接下来爬取时我们会使用它。
解析Response
在上文中说明了parse方法的参数resposne是start_urls里面的链接爬取后的结果。所以在parse方法中,我们可以直接对response包含的内容进行解析,比如看看请求结果的网页源代码,或者进一步分析源代码里面包含什么,或者找出结果中的链接进一步得到下一个请求。
观察网站,我们可以看到网页中既有我们想要的结果,又有下一页的链接,所以两部分我们都要进行处理。
首先看一下网页结构,每一页都有多个class为quote的区块,每个区块内都包含text,author,tags,所以第一部需要找出所有的quote,然后对每一个quote进一步提取其中的内容。
提取的方式可以选用CSS选择器或XPath选择器,在这里我们使用CSS选择器进行选择,parse方法改写如下:
def parse(self, response):
quotes = response.css('.quote')
for quote in quotes:
text = quote.css('.text::text').extract_first()
author = quote.css('.author::text').extract_first()
tags = quote.css('.tags .tag::text').extract()
在这里使用了CSS选择器的语法,首先利用选择器选取所有的quote赋值为quotes变量。然后利用for循环对每个quote遍历,解析每个quote的内容。
对text来说,观察到它的class为text,所以可以用.text来选取,这个结果实际上是整个带有标签的元素,要获取它的内容,可以加::text来得到。这时的结果是大小为1的数组,所以还需要用extract_first方法来获取第一个元素,而对于tags来说,由于我们要获取所有的标签,所以用extract方法获取即可。
以第一个quote的结果为例,各个选择方法及结果归类如下:
&div class="quote" itemscope="" itemtype="http://schema.org/CreativeWork"&
&span class="text" itemprop="text"&“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”&/span&
&span&by &small class="author" itemprop="author"&Albert Einstein&/small&
&a href="/author/Albert-Einstein"&(about)&/a&
&div class="tags"&
&meta class="keywords" itemprop="keywords" content="change,deep-thoughts,thinking,world"&
&a class="tag" href="/tag/change/page/1/"&change&/a&
&a class="tag" href="/tag/deep-thoughts/page/1/"&deep-thoughts&/a&
&a class="tag" href="/tag/thinking/page/1/"&thinking&/a&
&a class="tag" href="/tag/world/page/1/"&world&/a&
quote.css('.text')
[&Selector xpath="descendant-or-self::*[@class and contains(concat(' ', normalize-space(@class), ' '), ' text ')]" data='&span class="text" itemprop="text"&“The '&]
quote.css('.text::text')
[&Selector xpath="descendant-or-self::*[@class and contains(concat(' ', normalize-space(@class), ' '), ' text ')]/text()" data='“The world as we have created it is a pr'&]
quote.css('.text').extract()
['&span class="text" itemprop="text"&“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”&/span&']
quote.css('.text::text').extract()
['“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”']
quote.css('.text::text').extract_first()
“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”
所以,对于text,要获取第一个元素即可,所以使用extract_first()方法,对于tags,要获取所有元素,使用extract()方法。
刚才定义了Item,接下来就要轮到使用它了,你可以把它理解为一个字典,不过在声明的时候需要实例化。然后依次对刚才解析的结果赋值,返回即可。
接下来QuotesSpider改写如下:
import scrapy
from tutorial.items import QuoteItem
class QuotesSpider(scrapy.Spider):
name = "quotes"
allowed_domains = [""]
start_urls = ['/']
def parse(self, response):
quotes = response.css('.quote')
for quote in quotes:
item = QuoteItem()
item['text'] = quote.css('.text::text').extract_first()
item['author'] = quote.css('.author::text').extract_first()
item['tags'] = quote.css('.tags .tag::text').extract()
yield item
如此一来,首页的所有内容就解析出来了,并赋值成了一个个QuoteItem。
后续Request
如上的操作实现了从初始页面抓取内容,不过下一页的内容怎样继续抓取?这就需要我们从该页面中找到信息来生成下一个请求,然后下一个请求的页面里找到信息再构造下一个请求,这样循环往复迭代,从而实现整站的爬取。
观察到刚才的页面拉到最下方,有一个Next按钮,查看一下源代码,可以发现它的链接是/page/2/,实际上全链接就是/page/2,通过这个链接我们就可以构造下一个请求。
构造请求时需要用到scrapy.Request,在这里我们传递两个参数,url和callback。
url,请求链接
callback,回调函数,当这个请求完成之后,获取到response,会将response作为参数传递给这个回调函数,回调函数进行解析或生成下一个请求,如上文的parse方法。
在这里,由于parse就是用来解析text,author,tags的方法,而下一页的结构和刚才已经解析的页面结构是一样的,所以我们还可以再次使用parse方法来做页面解析。
好,接下来我们要做的就是利用选择器得到下一页链接并生成请求,在parse方法后追加下面的代码。
next = response.css('.pager .next a::attr(href)').extract_first()
url = response.urljoin(next)
yield scrapy.Request(url=url, callback=self.parse)
第一句代码是通过CSS选择器获取下一个页面的链接,需要获取&a&超链接中的href属性,在这里用到了::attr(href)操作,通过::attr加属性名称我们可以获取属性的值。然后再调用extract_first方法获取内容。
第二句是调用了urljoin方法,它可以将相对url构造成一个绝对的url,例如获取到的下一页的地址是/page/2,通过urljoin方法处理后得到的结果就是/page/2/
第三句是通过url和callback构造了一个新的请求,回调函数callback依然使用的parse方法。这样在完成这个请求后,response会重新经过parse方法处理,处理之后,得到第二页的解析结果,然后生成第二页的下一页,也就是第三页的请求。这样就进入了一个循环,直到最后一页。
通过几行代码,我们就轻松地实现了一个抓取循环,将每个页面的结果抓取下来了。
现在改写之后整个Spider类是这样的:
import scrapy
from tutorial.items import QuoteItem
class QuotesSpider(scrapy.Spider):
name = "quotes"
allowed_domains = [""]
start_urls = ['/']
def parse(self, response):
quotes = response.css('.quote')
for quote in quotes:
item = QuoteItem()
item['text'] = quote.css('.text::text').extract_first()
item['author'] = quote.css('.author::text').extract_first()
item['tags'] = quote.css('.tags .tag::text').extract()
yield item
next = response.css('.pager .next a::attr("href")').extract_first()
url = response.urljoin(next)
yield scrapy.Request(url=url, callback=self.parse)
接下来让我们试着运行一下看看结果,进入目录,运行如下命令:
scrapy crawl quotes
就可以看到Scrapy的运行结果了。
2017-02-19 13:37:20 [scrapy.utils.log] INFO: Scrapy 1.3.0 started (bot: tutorial)
2017-02-19 13:37:20 [scrapy.utils.log] INFO: Overridden settings: {'NEWSPIDER_MODULE': 'tutorial.spiders', 'SPIDER_MODULES': ['tutorial.spiders'], 'ROBOTSTXT_OBEY': True, 'BOT_NAME': 'tutorial'}
2017-02-19 13:37:20 [scrapy.middleware] INFO: Enabled extensions:
['scrapy.extensions.logstats.LogStats',
'scrapy.extensions.telnet.TelnetConsole',
'scrapy.extensions.corestats.CoreStats']
2017-02-19 13:37:20 [scrapy.middleware] INFO: Enabled downloader middlewares:
['scrapy.downloadermiddlewares.robotstxt.RobotsTxtMiddleware',
'scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware',
'scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware',
'scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware',
'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware',
'scrapy.downloadermiddlewares.retry.RetryMiddleware',
'scrapy.downloadermiddlewares.redirect.MetaRefreshMiddleware',
'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware',
'scrapy.downloadermiddlewares.redirect.RedirectMiddleware',
'scrapy.downloadermiddlewares.cookies.CookiesMiddleware',
'scrapy.downloadermiddlewares.stats.DownloaderStats']
2017-02-19 13:37:20 [scrapy.middleware] INFO: Enabled spider middlewares:
['scrapy.spidermiddlewares.httperror.HttpErrorMiddleware',
'scrapy.spidermiddlewares.offsite.OffsiteMiddleware',
'scrapy.spidermiddlewares.referer.RefererMiddleware',
'scrapy.spidermiddlewares.urllength.UrlLengthMiddleware',
'scrapy.spidermiddlewares.depth.DepthMiddleware']
2017-02-19 13:37:20 [scrapy.middleware] INFO: Enabled item pipelines:
2017-02-19 13:37:20 [scrapy.core.engine] INFO: Spider opened
2017-02-19 13:37:20 [scrapy.extensions.logstats] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2017-02-19 13:37:20 [scrapy.extensions.telnet] DEBUG: Telnet console listening on 127.0.0.1:6023
2017-02-19 13:37:21 [scrapy.core.engine] DEBUG: Crawled (404) &GET /robots.txt& (referer: None)
2017-02-19 13:37:21 [scrapy.core.engine] DEBUG: Crawled (200) &GET /& (referer: None)
2017-02-19 13:37:21 [scrapy.core.scraper] DEBUG: Scraped from &200 /&
{'author': u'Albert Einstein',
'tags': [u'change', u'deep-thoughts', u'thinking', u'world'],
'text': u'\u201cThe world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.\u201d'}
2017-02-19 13:37:21 [scrapy.core.scraper] DEBUG: Scraped from &200 /&
{'author': u'J.K. Rowling',
'tags': [u'abilities', u'choices'],
'text': u'\u201cIt is our choices, Harry, that show what we truly are, far more than our abilities.\u201d'}
2017-02-19 13:37:27 [scrapy.core.engine] INFO: Closing spider (finished)
2017-02-19 13:37:27 [scrapy.statscollectors] INFO: Dumping Scrapy stats:
{'downloader/request_bytes': 2859,
'downloader/request_count': 11,
'downloader/request_method_count/GET': 11,
'downloader/response_bytes': 24871,
'downloader/response_count': 11,
'downloader/response_status_count/200': 10,
'downloader/response_status_count/404': 1,
'dupefilter/filtered': 1,
'finish_reason': 'finished',
'finish_time': datetime.datetime(2017, 2, 19, 5, 37, 27, 227438),
'item_scraped_count': 100,
'log_count/DEBUG': 113,
'log_count/INFO': 7,
'request_depth_max': 10,
'response_received_count': 11,
'scheduler/dequeued': 10,
'scheduler/dequeued/memory': 10,
'scheduler/enqueued': 10,
'scheduler/enqueued/memory': 10,
'start_time': datetime.datetime(2017, 2, 19, 5, 37, 20, 321557)}
2017-02-19 13:37:27 [scrapy.core.engine] INFO: Spider closed (finished)
在这里贴出部分运行结果,中间的一些抓取结果输出已省略。
首先Scrapy输出了当前的版本号,启动的项目。其次输出了当前在settings.py中的一些重写后的配置。然后输出了当前所应用的middlewares和pipelines,middlewares是默认启用的,如果要修改,我们可以在settings.py中修改,pipelines默认是空,同样也可以在settings.py中配置,后面会进行讲解。
再接下来就是输出各个页面的抓取结果了,可以看到它一边解析,一边翻页,直至将所有内容抓取完毕,然后终止。
在最后Scrapy输出了整个抓取过程的统计信息,如请求的字节数,请求次数,响应次数,完成原因等等。
这样整个Scrapy程序就成功运行完毕了。
可以发现我们通过非常简单的一些代码就完成了一个网站内容的爬取,相比之前自己一点点写程序是不是简洁太多了?
保存到文件
刚才运行完Scrapy后,我们只在控制台看到了输出结果,如果想将结果保存该怎么办呢?
比如最简单的形式,将结果保存成Json文件。
要完成这个其实不需要你写任何额外的代码,Scrapy提供了Feed Exports可以轻松地将抓取结果输出,例如我们想将上面的结果保存成Json文件,可以执行如下命令:
运行后发现项目内就会多了一个quotes.json文件,里面包含的就是刚才抓取的所有内容,是一个Json格式,多个项目由中括号包围,是一个合法的Json格式。
另外你还可以每一个Item一个Json,最后的结果没有中括号包围,一行对应一个Item,命令如下:
.jsonlines
另外还支持很多格式输出,例如csv,xml,pickle,marshal等等,还支持ftp,s3等远程输出,另外还可以通过自定义ItemExporter来实现其他的输出。
例如如下命令分别对应输出为csv,xml,pickle,marshal,格式以及ftp远程输出:
scrapy crawl quotes -o quotes.csv
scrapy crawl quotes -o quotes.xml
scrapy crawl quotes -o quotes.pickle
scrapy crawl quotes -o quotes.marshal
scrapy crawl quotes -o ftp://user:pass@ftp./path/to/quotes.csv
其中ftp输出需要你正确配置好你的用户名,密码,地址,输出路径,否则会报错。
通过Scrapy提供的Feed Exports我们可以轻松地输出抓取结果到文件,对于一些小型项目这应该是足够了,不过如果想要更复杂的输出,如输出到数据库等等,你可以使用Item Pileline更方便地实现。
使用Item Pipeline
至此,你已经可以成功地完成抓取并将结果保存了,如果你想进行更复杂的操作,如将结果保存到数据库,如MongoDB,或者筛选某些有用的Item,可以定义Item Pileline来实现。
Item Pipeline意为项目管道,当生成Item后,它会自动被送到Item Pipeline进行处理,我们常用它来做如下操作:
清理HTML数据
验证爬取数据,检查爬取字段
查重并丢弃重复内容
将爬取结果储存到数据库
要实现一个Item Pipeline很简单,只需要定义一个类并实现process_item方法即可,启用后,Item Pipeline会自动调用这个方法,这个方法必须返回包含数据的字典或是Item对象,或者抛出DropItem异常。
这个方法由两个参数,一个是item,每次Spider生成的Item都会作为参数传递过来,另一个是spider,就是Spider的实例。
好,接下来我们实现一个Item Pipeline,筛掉text长度大于50的Item并将结果保存到MongoDB。
修改项目里的pipelines.py文件,之前自动生成的可以删掉,增加一个TextPipeline类,内容如下:
from scrapy.exceptions import DropItem
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() + '...'
return item
return DropItem('Missing Text')
在构造方法里面定义了限制长度,长度限制为50,然后实现了process_item方法,参数是item和spider,首先判断item的text属性是否存在,如果不存在,那就跑出DropItem异常,如果存在,再判断长度是否大于50,如果大于,那就截断然后拼接省略号,再将item返回即可。
接下来,我们再将处理后的item存入MongoDB,如果你还没有安装,请先安装好MongoDB。
另外还需要安装一个MongoDB开发包pymongo,利用pip安装即可:
pip3 install pymongo
接下来定义另外一个Pipeline,同样在pipelines.py中,实现另一个类MongoPipeline,内容如下:
import pymongo
class MongoPipeline(object):
def __init__(self, mongo_uri, mongo_db):
self.mongo_uri = mongo_uri
self.mongo_db = mongo_db
@classmethod
def from_crawler(cls, crawler):
return cls(
mongo_uri=crawler.settings.get('MONGO_URI'),
mongo_db=crawler.settings.get('MONGO_DB')
def open_spider(self, spider):
self.client = pymongo.MongoClient(self.mongo_uri)
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()
在这个类中,实现了API定义的另外几个方法。
from_crawler,这是一个类方法,用@classmethod标识,是一种依赖注入的方式,方法的参数就是crawler,通过crawler这个我们可以拿到全局配置的每个配置信息,在全局配置settings.py中我们可以定义MONGO_URI和MONGO_DB来指定MongoDB连接需要的地址和数据库名称,拿到配置信息之后返回类对象即可。所以这个方法的定义主要是用来获取settings.py中的配置的。
open_spider,当spider被开启时,这个方法被调用。在这里主要进行了一些初始化操作。
close_spider,当spider被关闭时,这个方法会调用,在这里将数据库连接关闭。
那么最主要的process_item方法则执行了数据插入操作。
好,定义好这两个类后,我们需要在settings.py中使用他们,还需要定义MongoDB的连接信息。
在settings.py中加入如下内容:
ITEM_PIPELINES = {
'tutorial.pipelines.TextPipeline': 300,
'tutorial.pipelines.MongoPipeline': 400,
MONGO_URI='localhost'
MONGO_DB='tutorial'
赋值ITEM_PIPELINES字典,键名是Pipeline的类名称,键值是调用优先级,数字越小越先被调用。
定义好了之后,再重新执行爬取,命令如下:
scrapy crawl quotes
爬取结束后,可以观察到MongoDB中创建了一个tutorial的数据库,QuoteItem的表。
到现在,我们就通过抓取quotes完成了整个Scrapy的简单入门,但这只是冰山一角,还有很多内容等待我们去探索,后面会进行讲解。
本节代码:
本文仅代表作者观点,不代表腾讯云技术社区官方立场。
本文系作者授权腾讯云技术社区发表,未经许可,不得转载。
近期精品热文

我要回帖

 

随机推荐