抓取网页新闻(:自然灾害滑坡的全部国内新闻,要求主题为滑坡类新闻)

优采云 发布时间: 2022-02-10 01:07

  抓取网页新闻(:自然灾害滑坡的全部国内新闻,要求主题为滑坡类新闻)

  目标陈述

  使用scrapy在Chinanews中抓取国内所有关于自然灾害和山体滑坡的新闻,方便后续的文本挖掘分析。

  网站分析

  目标网站:

  

  结合中新搜索平台的高级搜索功能,搜索关键词设置为:滑坡经济损失(空格分隔),分类频道设置为国内,排序方式根据相关性. 获取所有检索到的新闻如下:

  

  一共1000多条数据。

  分析网站的特性发现请求是异步加载的,通过抓包工具Fiddler获取:

  

  分析:

  POST提交,每个提交目标的url为:

  提交参数如上图,其中q代表关键词(抓包测试时只输入了一个关键词);

  ps 表示每次显示的调试信息,adv=1 表示高级搜索;day1,day2表示搜索时间,不写默认表示一直,channel=gn表示国内;

  继续点击下一页,得到一个新参数:比较开始

  分析显示,每次点击下一页,都是POST提交,参数相同,但不同的是start

  代码逻辑

  使用命令:scrapy start project NewsChina 创建项目,编写items.py文件,并清晰捕获字段(常用套路,第一步是清晰捕获字段)。这里只抓取标题和正文。常规操作:添加数据源和爬取时间信息。

  # -*- coding: utf-8 -*-

# Define here the models for your scraped items

#

# See documentation in:

# https://doc.scrapy.org/en/latest/topics/items.html

import scrapy

class NewschinaItem(scrapy.Item):

# define the fields for your item here like:

# name = scrapy.Field()

# 数据来源

source = scrapy.Field()

# 抓取时间

utc_time = scrapy.Field()

# 新闻标题

title = scrapy.Field()

# 新闻内容

content = scrapy.Field()

# 关键词

keywords = scrapy.Field()

  明确抓取字段后,使用命令:scrapy genspider newsChina 生成爬虫,开始编写爬虫逻辑,结合抓取网站的特点如下:

  对于这个网站的防爬措施,增加请求延迟、重试次数和等待配置;

  通过修改POST请求的time_scope字段,获取每页数据,解析数据中详情页的链接,然后对详情页进行链接请求,解析待爬取的数据;

  至于循环抓取和终止循环条件,根据实际网站有所不同,代码中已经说明。

  # -*- coding: utf-8 -*-

import re

import scrapy

from NewsChina.items import NewschinaItem

class NewschinaSpider(scrapy.Spider):

name = 'newsChina'

# allowed_domains = ['sou.chinanews.com']

# start_urls = ['http://http://sou.chinanews.com/']

#爬虫设置

# handle_httpstatus_list = [403] # 403错误时抛出异常

custom_settings = {

"DOWNLOAD_DELAY": 2,

"RETRY_ENABLED": True,

}

page = 0

# 提交参数

formdata = {

'field': 'content',

'q': '滑坡 经济损失',

'ps': '10',

'start': '{}'.format(page * 10),

'adv': '1',

'time_scope': '0',

'day1': '',

'day2': '',

'channel': 'gn',

'creator': '',

'sort': '_score'

}

# 提交url

url = 'http://sou.chinanews.com/search.do'

def start_requests(self):

yield scrapy.FormRequest(

url=self.url,

formdata=self.formdata,

callback=self.parse

)

def parse(self, response):

try:

last_page = response.xpath('//div[@id="pagediv"]/span/text()').extract()[-1]

# 匹配到尾页退出迭代

if last_page is '尾页':

return

except:

# 当匹配不到last_page时,说明已经爬取所有页面,xpath匹配失败

# 抛出异常,这就是我们的循环终止条件

# print("last_page:", response.url)

return

link_list = response.xpath('//div[@id="news_list"]/table//tr/td/ul/li/a/@href').extract()

for link in link_list:

if link:

item = NewschinaItem()

# 访问详情页

yield scrapy.Request(link, callback=self.parse_detail, meta={'item': item})

# 循环调用,访问下一页

self.page += 1

# 下一页的开始,修改该参数得到新数据

self.formdata['start'] = '{}'.format(self.page * 10)

yield scrapy.FormRequest(

url=self.url,

formdata=self.formdata,

callback=self.parse

)

# 从详情页中解析数据

def parse_detail(self, response):

"""

分析发现,中新网年份不同,所以网页的表现形式不同,

由于抓取的是所有的数据,因此同一个xpath可能只能匹配到部分的内容;

经过反复测试发现提取规则只有如下几条。提取标题有两套规则

提取正文有6套规则。

:param response:

:return:

"""

item = response.meta['item']

# 提取标题信息

if response.xpath('//h1/text()'):

item['title'] = response.xpath('//h1/text()').extract_first().strip()

elif response.xpath('//title/text()'):

item['title'] = response.xpath('//title/text()').extract_first().strip()

else:

print('title:', response.url)

# 提取正文信息

try:

if response.xpath('//div[@id="ad0"]'):

item['content'] = response.xpath('//div[@id="ad0"]').xpath('string(.)').extract_first().strip()

elif response.xpath('//div[@class="left_zw"]'):

item['content'] = response.xpath('//div[@class="left_zw"]').xpath('string(.)').extract_first().strip()

elif response.xpath('//font[@id="Zoom"]'):

item['content'] = response.xpath('//font[@id="Zoom"]').xpath('string(.)').extract_first().strip()

elif response.xpath('//div[@id="qb"]'):

item['content'] = response.xpath('//div[@id="qb"]').xpath('string(.)').extract_first().strip()

elif response.xpath('//div[@class="video_con1_text_top"]/p'):

item['content'] = response.xpath('//div[@class="video_con1_text_top"]/p').xpath('string(.)').extract_first().strip()

else:

print('content:', response.url)

except:

# 测试发现中新网有一个网页的链接是空的,因此提前不到正文,做异常处理

print(response.url)

item['content'] = ''

yield item

  编写中间件并添加随机头信息:

  # -*- coding: utf-8 -*-

# Define here the models for your spider middleware

#

# See documentation in:

# https://doc.scrapy.org/en/latest/topics/spider-middleware.html

import random

from NewsChina.settings import USER_AGENTS as ua

class NewsChinaSpiderMiddleware(object):

def process_request(self, request, spider):

"""

给每一个请求随机分配一个代理

:param request:

:param spider:

:return:

"""

user_agent = random.choice(ua)

request.headers['User-Agent'] = user_agent

  编写数据保存逻辑:

  结合python的jieba模块的textrank算法,可以提取新闻关键词保存到excel或者数据库

  # -*- coding: utf-8 -*-

# Define your item pipelines here

#

# Don't forget to add your pipeline to the ITEM_PIPELINES setting

# See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html

from datetime import datetime

from jieba import analyse

from openpyxl import Workbook

import pymysql

class KeyswordPipeline(object):

"""

添加数据来源及抓取时间;

结合textrank算法,抽取新闻中最重要的5个词,作为关键词

"""

def process_item(self, item, spider):

# 数据来源

item['source'] = spider.name

# 抓取时间

item['utc_time'] = str(datetime.utcnow())

content = item['content']

keywords = ' '.join(analyse.textrank(content, topK=5))

# 关键词

item['keywords'] = keywords

return item

class NewsChinaExcelPipeline(object):

"""

数据保存

"""

def __init__(self):

self.wb = Workbook()

self.ws = self.wb.active

self.ws.append(['标题', '关键词', '正文', '数据来源', '抓取时间'])

def process_item(self, item, spider):

data = [item['title'], item['keywords'], item['content'], item['source'], item['utc_time']]

self.ws.append(data)

self.wb.save('./news.xls')

return item

# class NewschinaPipeline(object):

# def __init__(self):

# self.conn = pymysql.connect(

# host='.......',

# port=3306,

# database='news_China',

# user='z',

# password='136833',

# charset='utf8'

# )

# # 实例一个游标

# self.cursor = self.conn.cursor()

#

# def process_item(self, item, spider):

# sql = """

# insert into ChinaNews(ID, 标题, 关键词, 正文, 数据来源, 抓取时间)

# values (%s, %s, %s, %s, %s, %s);"""

#

# values = [

# item['title'],

# item['keywords'],

# item['content'],

#

# item['source'],

# item['utc_time']

# ]

#

# self.cursor.execute(sql, values)

# self.conn.commit()

#

# return item

#

# def close_spider(self, spider):

# self.cursor.close()

# self.conn.close()

  运行结果

  

  完整代码

  也可以看看:

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线