1.爬取分析
打开阳光问政平台的最新问政板块,可以看到问政问题列表。
打开浏览器开发者工具,每个问题的内容就放在< li >标签中,我们要提取span里的内容。
将网页滑到最下面,可以看到翻页条,我们要提取下一页的链接进行翻页。
< a >标签中的链接即为下一页的链接。
点击其中一个问题的链接,打开详情页。
我们要提取< pre >标签中的内容。
爬取思路:
- 提取主页的每条问题的编号、问政标题、问题链接等内容
- 打开每条问题的详情页,提取问题详细内容
- 循环翻页爬取
2.代码实现
(1)创建项目
创建一个新的Scrapy项目,进入自定义的项目目录中,运行下列命令:
scrapy startproject sunwz
进入sunwz目录,创建一个名为sun的爬虫,运行下列命令:
scrapy genspider sun "sun0769.com"
(2)修改items.py
import scrapy
class SunwzItem(scrapy.Item):
number = scrapy.Field() #编号
status = scrapy.Field() #状态
title = scrapy.Field() #问政标题
response_time = scrapy.Field() #响应时间
question_time = scrapy.Field() #问政时间
question_link = scrapy.Field() #问题链接
content = scrapy.Field() #问题内容
(3)修改sun.py
基本逻辑是:使用parse方法解析主页数据,然后打开详情页,调用parse_detail方法解析详情页数据,最后将item返回,每一个item就是一个问题。
import scrapy
from sunwz.items import SunwzItem
class SunSpider(scrapy.Spider):
name = 'sun'
allowed_domains = ['sun0769.com']
start_urls = ['http://wz.sun0769.com/political/index/politicsNewest?id=1']
base_url = 'http://wz.sun0769.com'
def parse(self, response):
questions = response.css('.title-state-ul .clear')
for question in questions:
item = SunwzItem()
item['number'] = question.css('.state1::text').extract_first()
item['status'] = question.css('.state2::text').extract_first().strip()
item['title'] = question.css('.state3 .color-hover::text').extract_first()
item['response_time'] = question.css('.state4::text').extract_first().strip()
item['question_time'] = question.css('.state5::text').extract_first()
item['question_link'] = self.base_url + question.css('.state3 .color-hover::attr(href)').extract_first()
yield scrapy.Request(url=item['question_link'], callback=self.parse_detail, meta={'item': item})
next = response.css('.arrow-page.prov_rota::attr(href)').extract_first()
url = self.base_url + next
yield scrapy.Request(url=url, callback=self.parse)
def parse_detail(self, response):
item = response.meta['item']
item['content'] = response.css('.details-box pre::text').extract_first()
yield item
(4)保存为csv
运行下面命令,可将提取到的数据保存到csv文件。
scrapy crawl sun -o sun.csv
(5)保存到MongoDB
修改settings.py
SPIDER_MODULES = ['sunwz.spiders']
NEWSPIDER_MODULE = 'sunwz.spiders'
MONGO_URI = 'localhost'
MONGO_DB = 'sunwz' #数据库名
ITEM_PIPELINES = {
'sunwz.pipelines.SunwzPipeline': 300,
'sunwz.pipelines.MongoPipeline': 400,
}
修改pipelines.py
import pymongo
class SunwzPipeline(object):
def process_item(self, item, spider):
return item
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()
MongoDB数据库中的数据。
3.制作词云图
import csv
import matplotlib.pyplot as plt
import jieba
from wordcloud import WordCloud
#1.读出
text = ''
csv_file = csv.reader(open('sun.csv',encoding='utf-8'))
for row in csv_file:
text = text + row[0]
#2.剪开
cut_text = jieba.cut(text)
stopwords = {'可以','已经','这些','虽然','如果','这个','所以','现在',
'不是','怎么','甚至','时候','没有','什么','大家','自己',
'还是','不能','需要','为什么','而且','开始','一直','他们',
'或者','应该','我们','你们','但是','因为','这样','还有'}
cuttext = [i for i in cut_text if i not in stopwords]
#3.以空格拼接起来
result = " ".join(cuttext)
#生成词云
wc = WordCloud(
font_path='C:/Windows/fonts/simhei.ttf', #字体路径
background_color='white', #背景颜色
width=1000,
height=600,
max_font_size=50, #字体大小
min_font_size=10,
mask=plt.imread('bg.jpg'), #背景图片
max_words=1000
)
wc.generate(result)
wc.to_file('wordcloud.png') #图片保存
4.Scrapy知识补充
(1)常用命令
- startproject
scrapy startproject example
- genspider
scrapy genspider example "example.com"
- runspider
scrapy runspider example
- shell
scrapy shell http://www.baidu.com
之后可以直接执行命令
response.css("title").extract_first()
- fetch
scrapy fetch http://www.baidu.com
- crawl
scrapy crawl example
(2)CSS选择器
- 基本格式
response.css('.classname::text').extract_first() #根据class提取文本
response.css('.classname1.classname2::text').extract_first() #有两个class
response.css('p::text').extract_first() #根据标签提取文本
response.css('a::attr(href)').extract_first() #提取属性
- 高级用法