本节我们通过爬取一个新闻网站的新闻来介绍scrapy的pipeline,以及如何在pipeline中将爬取到的数据写入到数据库中。
首先是我们要爬取的内容是
https://www.chinanews.com
这个网站首页的所有文章的标题和内容。
数据表字段只要3个:
id,title和content
接下来正式开始:
创建scrapy项目
scrapy startproject qqnews
为啥起名叫qqnews呢,因为本来打算爬取腾讯网的新闻,后来有改主意了,哈哈哈哈,不过不用在意这些细节。
首先我们要先定义容器:
qqnews/items.py 内容如下
# -*- coding: utf-8 -*-
import scrapy
class QqnewsItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
title = scrapy.Field()
content = scrapy.Field()
容器中只有两个字段,title和content
接下来我们正式编写spider:
在 qqnews/spiders目录中创建一个qqnews_spider.py文件
内容如下:
# coding = utf-8
#取出https://news.qq.com首页的所有文章的文章详情页的标题,内容,和文章来源,并存入到数据库中
import scrapy
from qqnews.items import QqnewsItem
class QqnewsSpider(scrapy.Spider):
name="qqnews"
start_urls=["https://www.chinanews.com"]
allowed_domains=["www.chinanews.com"]
def parse(self,res):
#获取首页所有详情页的链接
urls=res.xpath(r"//a[re:test(@href,'//www.chinanews.com/[a-zA-z]+/\d+/\d+-\d+/\d+\.s?html')]/@href").extract()
for url in urls:
if url.find("http") == -1:
url="https:"+url
# print(url)
yield scrapy.Request(url,callback=self.parse_news) #Request会请求获取到的文章链接,callback=self.parse_news表示将请求到的页面内容传给parse_news方法
def parse_news(self,news):
#获取标题和内容
qqnew_item=QqnewsItem()
qqnew_item['title']=news.xpath("//div[@class='content']/h1/text()").extract_first()
qqnew_item['content']='\n'.join(news.xpath("//div[@class='left_zw']/p/text()").extract())
yield qqnew_item
蜘蛛启动后执行的第一个方法就是parse方法,所以,请不要修改parse方法的方法名,其他方法都可以自定义名称,但parse方法名请不要动。
在parse方法中,定义了获取所有首页文章a标签的xpath规则,在xpath中也是支持使用正则的。
上面parse方法负责获取所有的文章链接,并请求这些链接并将请求到的页面源代码内容传给parse_news方法;而parse_news方法则负责获取文章详情页的标题和文章正文并存到item容器中,并返回。
parse_news是将文章都爬取到了,但是我们要控制它将文章存到数据库中,这里就要使用到pipeline
它位于scrapy项目目录的pipelines.py
这个文件的作用是可以对抓取到的内容进行处理,比如你已经抓取到了内容和标题,现在你想做一下事情:
a.对数据进行过滤,或者对数据的格式进行调整
b.将数据存到数据库中
这些都可以在这个文件中进行
当然在pipeline中不仅仅可以做数据存储,还可以做很多事情,比如数据的修改。
PS:pipelines.py默认是不会起作用的,如果想要起作用,要在settings.py将这个的注释去掉
还有像如果你要讲数据存到数据库中的话,数据库的配置信息也是写到这个文件中的。
所以接下来我们要修改setting.py文件
在setting.py添加数据库配置
DB_CONFIG={
"host":"127.0.0.1",
"user":"root",
"password":"你自己的数据库密码",
"database":"test",
"charset":"utf8"
}
在setting.py开启pipeline:
ITEM_PIPELINES = {
'qqnews.pipelines.QqnewsPipeline': 300,
}
写法是这样的:
'项目目录名.pipelines文件名.想开启的pipeline的类名':执行顺序 #执行顺序是个数字,数字越小,越先执行;pipelines文件可以不只一个,可以自定义,但是里面的写法要遵循原pipeline的写法
配置了setting之后,在pipelines.py进行数据库操作:
# -*- coding: utf-8 -*-
import json,pymysql
#导入settings.py中的配置信息
# from scrapy.conf import settings
class QqnewsPipeline(object):
def __init__(self):
self.data=[]
#连接数据库
def open_spider(self,spider):
# 连接
self.conn = pymysql.connect(**spider.settings.get('DB_CONFIG'))
# 创建光标
self.cursor = self.conn.cursor(cursor=pymysql.cursors.DictCursor)
def close_spider(self,spider):
#一次性将所有的数据插入
sql="insert into news values (%s,%s,%s)"
self.cursor.executemany(sql,self.data)
self.cursor.close()
self.conn.close()
def process_item(self, item, spider):
self.data.append((None,item['title'],item['content']))
spider.settings.get("DB_CONF")可以获取到你在setting中定义的数据库配置
PS:parse_news()一定要将qqnew_item容器返回,否则pipelines是获取不到容器中的数据的,到时候存到数据库的内容会为空。
如果想要在xpath中使用正则,就要像上面那样写,re:test()表示是使用re.test()方法,还可以re:match();
open_spider/close_spider/process_item 这三个方法是pipeline类中固定的方法,继承自父类;我们只需重写这三个方法即可;
这三个方触发的时机是:
open_spider 在蜘蛛启动时触发,所以从爬取开始到结束只执行一次。
close_spider 在蜘蛛关闭时触发,所以从爬取开始到结束只执行一次。
process_item 在蜘蛛返回item容器的时候触发,所以基本上是爬取一个详情页就出发一次。
上面的脚本都写好之后,只需在cmd执行
scrapy crawl dmoz
即可开始爬取
要注意的是:
Scrapy是会对链接自动去重的,不需要担心scrapy会爬取到重复的链接
Scrapy是多线程爬取,爬取的速度会比自己写的简单爬虫要快