scrapy抓取国家社科基金项目数据库
1.明确任务
目标网站:http://fz.people.com.cn/skygb/sk/index.php/Index/seach
抓取任务:抓取近五年某关键词(例如"能源"、”大数据“等)的搜索结果,抓取内容包括项目编号、项目名称、学科分类等十一个字段。
2.网站分析
利用Fiddler工具分析,输入关键词,指定年份后,点击搜索,针对有多页码搜索结果点击下一页,发现此过程共包括一个POST请求和一个GET请求;其中,指定关键词和年份点击搜索的过程为POST请求,点击下一页的过程为GET请求。
POST请求参数:
formdata = { 'xmname': '', 'xktype': '0', 'xmtype': '0', 'cglevel': '0', 'cbdate ': '0', 'cgxs': '0', 'lxtime': '', 'ssxt': '0', 'zyzw': '0', 'dwtype': '0', 'jxdata': '0', 'szdq': '0', 'pznum': '', 'cgname': '', 'jxnum': '', 'cbs': '', 'xmleader': '', 'hj': '', 'gzdw': '', 'zz': '' }
POST请求目标URL:http://fz.people.com.cn/skygb/sk/index.php/Index/seach
GET请求只需要指定参数p即可,因此构造GET请求url为:
n_url = self.url + '?' + 'xmname={}&p={}'.format(formdata['xmname'], self.page)
其中,xmname为搜索关键词,p为第几页的页码。
3.代码编写
3.1 创建scrapy爬虫项目
创建项目,名为ProSearch,使用命令:
scrapy startproject ProSearch http://fz.people.com.cn
3.2 明确抓取字段
创建爬虫项目后,编写items.py文件,明确待抓取的字段
# -*- coding: utf-8 -*- import scrapy class ProsearchItem(scrapy.Item): # 关键词 keyword = scrapy.Field() # 项目编号 pronums = scrapy.Field() # 项目类别 protype = scrapy.Field() # 学科类别 subtype = scrapy.Field() # 项目名称 proname = scrapy.Field() # 立项时间 protime = scrapy.Field() # 负责人 leaders = scrapy.Field() # 工作单位 workloc = scrapy.Field() # 单位类别 orgtype = scrapy.Field() # 所在省市 provloc = scrapy.Field() # 所属系统 systloc = scrapy.Field()
3.3 生成爬虫文件
在cmd窗口进入到ProSearch项目后,生成爬虫文件。使用命令:
scrapy genspider SearchPro
3.4 编写爬虫逻辑
生成爬虫文件后,来到spiders文件夹下的SearchPro.py文件,开始辨写爬虫逻辑。
# -*- coding: utf-8 -*- import scrapy from ProSearch.items import ProsearchItem class SearchproSpider(scrapy.Spider): name = 'SearchPro' # 爬虫名称 allowed_domains = ['fz.people.com.cn'] # start_urls = ['http://fz.people.com.cn/skygb/sk/index.php/Index/seach'] page = 1 # 指定检索关键词 keywords = ['可持续', '互联网', '大数据', '能源'] # 角标 index = 0 # 年份 years = 2018 # POST提交参数 formdata = { 'xmname': '', 'xktype': '0', 'xmtype': '0', 'cglevel': '0', 'cbdate ': '0', 'cgxs': '0', 'lxtime': '', 'ssxt': '0', 'zyzw': '0', 'dwtype': '0', 'jxdata': '0', 'szdq': '0', 'pznum': '', 'cgname': '', 'jxnum': '', 'cbs': '', 'xmleader': '', 'hj': '', 'gzdw': '', 'zz': '' } # 参数提交的url url = "http://fz.people.com.cn/skygb/sk/index.php/Index/seach" def start_requests(self): """ POST请求实现一般是重写start_requests函数,指定第一个关键词为默认检索关键词 :return: """ self.formdata['xmname'] = '可持续' self.formdata['lxtime'] = str(self.years) yield scrapy.FormRequest( url=self.url, formdata=self.formdata, callback=self.parse, meta={'formdata': self.formdata} ) def parse(self, response): """ 解析数据 :param response: :return: """ if response.meta['formdata']: formdata = response.meta['formdata'] # 每行数据所在节点 try: node_list = response.xpath("//div[@class='jc_a']/table/*")[1:] except: print("关键词‘{}’无搜索结果!".format(formdata['xmname'])) for node in node_list: # 提取数据 item = ProsearchItem() # 关键词 item['keyword'] = formdata['xmname'] # 项目编号 item['pronums'] = node.xpath('./td[1]/span/text()').extract_first() # 项目类别 item['protype'] = node.xpath('./td[2]/span/text()').extract_first() # 学科类别 item['subtype'] = node.xpath('./td[3]/span/text()').extract_first() # 项目名称 item['proname'] = node.xpath('./td[4]/span/text()').extract_first() # 立项时间 item['protime'] = node.xpath('./td[5]/span/text()').extract_first() # 负责人 item['leaders'] = node.xpath('./td[6]/span/text()').extract_first() # 工作单位 item['workloc'] = node.xpath('./td[8]/span/text()').extract_first() # 单位类别 item['orgtype'] = node.xpath('./td[9]/span/text()').extract_first() # 所属省市 item['provloc'] = node.xpath('./td[10]/span/text()').extract_first() # 所属系统 item['systloc'] = node.xpath('./td[11]/span/text()').extract_first() yield item # 匹配下一页的数据 if '下一页' in response.xpath("//div[@class='page clear']/a").extract(): self.page += 1 n_url = self.url + '?' + 'xmname={}&p={}'.format(formdata['xmname'], self.page) yield scrapy.Request(url=n_url, callback=self.parse, meta={'formdata': formdata}) # 匹配其他年份的数据 searcy_year = int(formdata['lxtime']) if not searcy_year <= 2014: searcy_year -= 1 formdata['lxtime'] = str(searcy_year) print("检索关键词:{}!".format(formdata['xmname'])) yield scrapy.FormRequest( url=self.url, formdata=formdata, callback=self.parse, meta={'formdata': formdata} ) # 其他关键词搜索 else: self.index += 1 if not self.index > len(self.keywords)-1: keyword = self.keywords[self.index] print("更新检索关键词为:{}".format(keyword)) formdata['xmname'] = keyword formdata['lxtime'] = str(self.years) yield scrapy.FormRequest( url=self.url, formdata=formdata, callback=self.parse, meta={'formdata': formdata} )
3.5 编写数据保存逻辑
本项目用excel对数据进行保存。在pipelines.py文件中编写数据保存的逻辑。
# -*- coding: utf-8 -*- from openpyxl import Workbook class ProsearchPipeline(object): def __init__(self): # 创建excel表格保存数据 self.workbook = Workbook() self.booksheet = self.workbook.active self.booksheet.append(['关键词', '项目编号', '项目类别', '学科类别', '项目名称', '立项时间', '负责人', '工作单位', '单位类别', '所在省市', '所属系统']) def process_item(self, item, spider): DATA = [ item['keyword'], item['pronums'], item['protype'], item['subtype'], item['proname'], item['protime'], item['leaders'], item['workloc'], item['orgtype'], item['provloc'], item['systloc'] ] self.booksheet.append(DATA) self.workbook.save('./data/ProSearch.xls') return item
3.6 其他细节
到此基本内容完成,设置一下settings.py文件对细节进行处理基本爬虫就可以运行了。
处理一:打开pipline通道
处理二:添加随机请求头并打开下载中间件
处理三:添加重拾、延时、log级别等
处理四:编写脚本main.py文件
scrapy默认使用命令行进行创建、生成、爬去等任务,可尝试在整个项目下编写一个main.py文件,将爬去命令添加到py文件中,直接在编辑器中F5运行。
下次每次运行main.py文件就可以直接运行了。
4.完整代码