本博客不介绍具体细节,详细入门教程可以看最下方的参考链接,本博客只介绍如何实现一个简单的能够利用百度搜索抓取和P2P相关新闻的爬虫。
安装Scrapy
pip install scrapy
或conda install scrapy
建立Scrapy项目
命令行中输入scrapy startproject p2p
建立一个名为"p2p"的scrapy项目,其目录结构如下:
p2p/
scrapy.cfg
p2p/
__init__.py
items.py
pipelines.py
settings.py
spiders/
__init__.py
...
其中items.py
用来定义数据结构,pipelines.py
用于对items进行处理并储存,本项目不使用pipline,spiders
里面是我们的爬虫代码,用于抓取数据。
修改配置文件
为了抓取百度新闻的内容,我们需要修改settings.py
配置文件,以下部分需要修改:
ROBOTSTXT_OBEY = False
,这是为了让爬虫不遵守robotstxt协议,默认百度是不让抓取的USER_AGENT = 'Mozilla/5.0'
,改成和你浏览器相对应的值,我的是Firefox浏览器,不然response会返回"favicon.ico"FEED_EXPORT_ENCODING = 'utf-8'
,防止使用Unicode编码,难以显示
定义Item
items.py
里的item类用于保存数据,例如:
import scrapy
class P2PItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
url = scrapy.Field()
title = scrapy.Field()
domain = scrapy.Field()
content = scrapy.Field()
我们保存新闻的url、标题、网站域名和内容
编写Spider类
在spiders
目录下创建p2p_spider.py
文件
import scrapy
import os
from p2p.items import P2PItem
import re
import logging
class P2PSpider(scrapy.Spider):
item_max = 20 #设一个抓取文章的上限,防止一直抓取
url_max = 1000 #设一个访问url的上限
item_num = 0
url_num = 0
name = "p2p"
allowed_domains = ["sohu.com","ifeng.com","news.baidu.com"] #将一些主要新闻网站加入抓取范围,当然baidu也得加入其中,因为翻页后还在baidu域名下面
start_urls = [
"https://news.baidu.com/ns?word=p2p" #在百度新闻搜索中搜“p2p”
]
def parse(self, response):
logging.info(f'enter baidu : {response.url}')
url_res = response.css("div.result h3.c-title a::attr(href)").extract() #提取新闻的url
url_page = response.css("p#page a::attr(href)").extract() #提取翻页的url
for u in url_res:
self.url_num += 1
if (self.url_num < self.url_max) and (self.item_num < self.item_max):
logging.debug(f'url id {self.url_num}/{self.url_max} : {u}')
if "sohu.com" in u:
yield scrapy.Request(u,callback=self.parse_sohu) #由于不同站点的html格式不一样,所以callback调用不同函数分别处理
elif "ifeng.com" in u:
yield scrapy.Request(u,callback=self.parse_ifeng)
for u in url_page:
u = response.urljoin(u)
self.url_num += 1
if (self.url_num < self.url_max) and (self.item_num < self.item_max):
logging.debug(f'url id {self.url_num}/{self.url_max} : {u}')
if "news.baidu.com" in u:
yield scrapy.Request(u,callback=self.parse)
def parse_sohu(self,response):
logging.info(f'parse sohu {self.item_num}/{self.item_max}: {response.url}')
title = response.css("div.text-title h1::text").extract_first()
content = ''.join(response.xpath("//article//text()").extract())
if (title is not None) and (content is not None):
item = P2PItem()
item['url'] = response.url
item['title'] = title.replace(' ','')
item['domain'] = 'sohu.com'
item['content'] = re.sub(r'\s+',r'\n',content.replace(' ',''))
self.item_num+=1
if self.item_num<self.item_max:
return item #返回数据结构
def parse_ifeng(self,response):
logging.info(f'parse ifeng {self.item_num}/{self.item_max}: {response.url}')
title = response.css("h1#artical_topic::text").extract_first()
content = ''.join(response.xpath("//div[@id='main_content']//text()").extract())
if (title is not None) and (content is not None):
item = P2PItem()
item['url'] = response.url
item['title'] = title.replace(' ','')
item['domain'] = 'ifeng.com'
item['content'] = re.sub(r'\s+',r'\n',content.replace(' ',''))
self.item_num+=1
if self.item_num<self.item_max:
return item
运行爬虫
进入包含scrapy.cfg
文件的目录,执行下面命令运行爬虫,其中“p2p”是爬虫的名字,在settings.py
中BOT_NAME = 'p2p'
设置,默认为工程名。并将结果保存成csv格式。
scrapy crawl p2p -o data/items.csv -L INFO
-L INFO
是只显示INFO
以上级别的信息,不然会有大量DEBUG
信息输出。
结果如下
我们程序中设的是抓20篇新闻,实际只抓到了15篇,这是由于我们只抓搜狐和凤凰的新闻,而凤凰网只有财经板块能抓到,其他板块HTML格式不一样,需要单独配置。