1、创建Scrapy项目
scrapy startproject Stats
2.进入项目目录,使用命令genspider创建Spider
scrapy genspider stats stats.gov.cn
3、定义要抓取的数据(处理items.py文件)
# -*- coding: utf-8 -*-
import scrapy
class StatsItem(scrapy.Item):
# 第一级名称,各个省、直辖市
first_titles = scrapy.Field()
# 第一级url
first_urls = scrapy.Field()
# 第一级存储目录
first_filename = scrapy.Field()
# 第二级名称,市、县
second_titles = scrapy.Field()
# 第二级url
second_urls = scrapy.Field()
# 第二级代码ID
second_id = scrapy.Field()
# 二级存储目录
second_filename = scrapy.Field()
# 第三级名称,区
third_titles = scrapy.Field()
# 第三级url
third_urls = scrapy.Field()
# 第三级代码ID
third_id = scrapy.Field()
# 三级存储目录
third_filename = scrapy.Field()
# 第四级名称,办事处
fourth_titles = scrapy.Field()
# 第四级url
fourth_urls = scrapy.Field()
# 第四级代码ID
fourth_id = scrapy.Field()
# 四级存储目录
fourth_filename = scrapy.Field()
# 第五级名称,村,居委会
fifth_titles = scrapy.Field()
# 第五级代码ID
fifth_id = scrapy.Field()
# 五级存储目录
fifth_filename = scrapy.Field()
4、编写提取item数据的Spider(在spiders文件夹下:stats.py)
# -*- coding: utf-8 -*-
# 爬取统计局的城乡代码
import scrapy
import os
from Stats.items import StatsItem
class StatsSpider(scrapy.Spider):
name = 'stats'
allowed_domains = ['stats.gov.cn']
start_urls = ['http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2016/index.html']
# 各个省、直辖市url前缀
url = 'http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2016/'
def parse(self, response):
print("处理第一级数据……")
items = []
# 第一级名称,各个省、直辖市(这里可以修改规则,爬取一部分省份)
first_titles = response.xpath('//tr//td/a/text()').extract()
# 第一级url>>>13.html
first_urls_list = response.xpath('//tbody//table//tr//td//a//@href').extract()
# 爬取第一级名称,各个省、直辖市
for i in range(0, len(first_titles)):
item = StatsItem()
# 指定第一级目录的路径和目录名
first_filename = "./Data/" + first_titles[i]
#如果目录不存在,则创建目录
if(not os.path.exists(first_filename)):
os.makedirs(first_filename)
item['first_filename'] = first_filename
# 保存第一级名称和url,但是url需要补全
item['first_titles'] = first_titles[i]
# http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2016/13.html
item['first_urls'] = self.url + first_urls_list[i]
items.append(item)
# 发送第一级爬取的url
for item in items:
yield scrapy.Request(url=item['first_urls'],meta={'meta_1': item},callback= self.second_parse)
# 处理第二级名称以及url,市、县
def second_parse(self,response):
print("处理第二级数据……")
# 提取每次Response的meta数据
meta_1 = response.meta['meta_1']
# 提取第二级的名称、url及id
second_titles = response.xpath('//tr//td[2]/a/text()').extract()
# 第二级url>>>>13/1301.html
second_urls_list = response.xpath('//tbody//table//tr//td[2]//a//@href').extract()
second_id = response.xpath('//tr//td[1]/a/text()').extract()
items = []
for i in range(0,len(second_urls_list)):
# url拼接>>>http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2016/13/1301.html
second_urls = self.url + second_urls_list[i]
# 第一级链接地址:http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2016/13.html
# 如果属于第一级相应省、直辖市,将存储目录放在相应文件夹中,第一级的链接去掉.html才能判断是否属于
#例如:修改后第一级链接http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2016/13 和
# 第二级链接http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2016/13/1301.html 这样才可以判断
if_belong = second_urls.startswith(meta_1['first_urls'][:-5])
if (if_belong):
# 目录类似于:\Data\河北省\河北省 石家庄市
second_filename = meta_1['first_filename']+"/"+ meta_1['first_titles']+" " + second_titles[i]
# 如果目录不存在,则创建目录
if (not os.path.exists(second_filename)):
os.makedirs(second_filename)
item = StatsItem()
item['first_titles'] = meta_1['first_titles']
item['first_urls'] = meta_1['first_urls']
item['second_titles'] = meta_1['first_titles']+" " + second_titles[i]
item['second_urls'] = second_urls
item['second_id'] = second_id[i]
item['second_filename'] = second_filename
items.append(item)
# 发送第二级url
for item in items:
yield scrapy.Request(url= item['second_urls'],meta={'meta_2':item},callback= self.third_parse)
# 处理第三级数据名称,区
def third_parse(self,response):
print("处理第三级数据……")
# 提取每次Response的meta数据
meta_2 = response.meta['meta_2']
# 提取第三级的名称、url及id
third_titles = response.xpath('//tr//td[2]/a/text()').extract()
# 第三级url>>>01/130102.html http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2016/13/01/130102.html
third_urls_list = response.xpath('//tbody//table//tr//td[2]//a//@href').extract()
third_id = response.xpath('//tr//td[1]/a/text()').extract()
items = []
for i in range(0,len(third_urls_list)):
# url拼接>>>http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2016/13/01/130102.html
third_urls = self.url + meta_2['second_id'][:2] + "/" + third_urls_list[i]
# 第二级链接地址:http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2016/13/1301.html
# http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2016/11/1101.html
# http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2016/34/3415.html
# 如果属于第二级相应市、县,将存储目录放在相应文件夹中,第二级的链接最后两位数字01是否和
# 第三级链接倒数第14位至倒数第13位是否相等(或者第三级链接倒数第9位至第8位)
# 或者:第二级链接最后4位(1301)和第三级链接倒数第11位至第8位是否相等
# http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2016/13/01/130102.html
# http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2016/11/01/110101.html
# http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2016/34/15/341502.html
# if_belong = third_urls.startswith(meta_2['second_urls'][:-9])
if (meta_2['second_urls'][-7:-5])==(third_urls[-14:-12]):
# if (if_belong):
# 目录类似于:\Data\河北省\河北省 石家庄市\河北省 石家庄市 长安区 130102000000
third_filename=meta_2['second_filename']+'/'+meta_2['second_titles']+" "+third_titles[i]+" "+third_id[i]
# 如果目录不存在,则创建目录
if (not os.path.exists(third_filename)):
os.makedirs(third_filename)
item = StatsItem()
item['first_titles'] = meta_2['first_titles']
item['first_urls'] = meta_2['first_urls']
item['second_titles'] = meta_2['second_titles']
item['second_urls'] = meta_2['second_urls']
item['second_id'] = meta_2['second_id']
item['second_filename'] = meta_2['second_filename']
item['third_titles'] = meta_2['second_titles'] + " "+third_titles[i]+" "+third_id[i]
item['third_urls'] = third_urls
item['third_id'] = third_id[i]
item['third_filename'] = third_filename
items.append(item)
# 发送第三级url
for item in items:
yield scrapy.Request(url=item['third_urls'],meta={'meta_3':item},callback=self.fourth_parse)
# 处理第四级数据名称,办事处
def fourth_parse(self,response):
print("处理第四级数据……")
# 提取每次Response的meta数据
meta_3 = response.meta['meta_3']
# 提取第四级的名称、url及id
fourth_titles = response.xpath('//tr//td[2]/a/text()').extract()
# 第四级url>>>02/130102001.html http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2016/13/01/02/130102001.html
fourth_urls_list = response.xpath('//tbody//table//tr//td[2]//a//@href').extract()
fourth_id = response.xpath('//tr//td[1]/a/text()').extract()
items = []
for i in range(0,len(fourth_urls_list)):
# url拼接>>>http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2016/13/01/02/130102001.html
fourth_urls = self.url + meta_3['third_id'][:2] + "/" + meta_3['third_id'][2:4]+"/"+fourth_urls_list[i]
# 第三级链接地址:http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2016/13/01/130102.html
# http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2016/13/01/130104.html
# http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2016/11/01/110101.html
# http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2016/11/01/110102.html
# 如果属于第三级相应区,将存储目录放在相应文件夹中,第三级链接最后两位数字02和第四级链接倒数第17位至
# 倒数第16位是否相等来判断(或者第四级链接的倒数第10位至倒数第9位)
# 或者第三级链接最后6位数字(130102)和第四级链接倒数第14位至倒数第9位是否相等
# 第四级链接http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2016/13/01/02/130102001.html
# http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2016/13/01/04/130104001.html
# http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2016/11/01/01/110101001.html
# http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2016/11/01/02/110102001.html
if (meta_3['third_urls'][-7:-5]) == fourth_urls[-17:-15]:
# 最后一级目录不应该出现上一级目录结尾的数字和最后一个空格,即: 130102000000,要切掉
# 目录类似于:\Data\河北省\河北省 石家庄市\河北省 石家庄市 长安区 130102000000\河北省 石家庄市 长安区 建北街道办事处 130102001000
m = meta_3['third_titles']
fourth_filename=meta_3['third_filename']+'/'+m[:m.rfind(" ")]+" "+fourth_titles[i]+" "+fourth_id[i]
# 如果目录不存在,则创建
if (not os.path.exists(fourth_filename)):
os.makedirs(fourth_filename)
item = StatsItem()
item['first_titles'] = meta_3['first_titles']
item['first_urls'] = meta_3['first_urls']
item['second_titles'] = meta_3['second_titles']
item['second_urls'] = meta_3['second_urls']
item['second_id'] = meta_3['second_id']
item['second_filename'] = meta_3['second_filename']
item['third_titles'] = meta_3['third_titles']
item['third_urls'] = meta_3['third_urls']
item['third_id'] = meta_3['third_id']
item['third_filename'] = meta_3['third_filename']
item['fourth_titles'] = m[:m.rfind(" ")]+" "+fourth_titles[i]+" "+fourth_id[i]
item['fourth_urls'] = fourth_urls
item['fourth_id'] = fourth_id[i]
item['fourth_filename'] = fourth_filename
items.append(item)
# 发送第四级url
for item in items:
yield scrapy.Request(url=item['fourth_urls'],meta={"meta_4":item},callback=self.fifth_parse)
# 处理第五级数据名称,村、居委会
def fifth_parse(self,response):
print("处理第五级数据……")
#提取每次Response的meta数据
meta_4 = response.meta['meta_4']
# 提取第五级的名称及id
fifth_titles = response.xpath('//tr[@class="villagetr"]//td[3]/text()').extract()
fifth_id = response.xpath('//tr[@class="villagetr"]//td[1]/text()').extract()
items = []
for i in range(0, len(fifth_titles)):
# 因为最后一级没有url链接,所以可以判断本页的url地址和上次传的url地址是否一样
if (response.url == meta_4['fourth_urls']):
# 目录类似于:\Data\河北省\河北省 石家庄市\河北省 石家庄市 长安区 130102000000\河北省 石家庄市 长安区 建北街道办事处 130102001000\
# 河北省 石家庄市 长安区 建北街道办事处 棉一社区居民委员会 130102001001
# # 最后一级目录不应该出现上一级目录结尾的数字和最后一个空格,即: 130102001000,要切掉
m = meta_4['fourth_titles']
fifth_filename = meta_4['fourth_filename']+"/"+m[:m.rfind(" ")]+" "+fifth_titles[i]+" "+fifth_id[i]
if (not os.path.exists(fifth_filename)):
os.makedirs(fifth_filename)
item = StatsItem()
# item['first_titles'] = meta_4['first_titles']
# item['first_urls'] = meta_4['first_urls']
# item['second_titles'] = meta_4['second_titles']
# item['second_urls'] = meta_4['second_urls']
# item['second_id'] = meta_4['second_id']
# item['second_filename'] = meta_4['second_filename']
item['third_titles'] = meta_4['third_titles']
# item['third_urls'] = meta_4['third_urls']
item['third_id'] = meta_4['third_id']
# item['third_filename'] = meta_4['third_filename']
item['fourth_titles'] = meta_4['fourth_titles']
# item['fourth_urls'] = meta_4['fourth_urls']
item['fourth_id'] = meta_4['fourth_id']
# item['fourth_filename'] = meta_4['fourth_filename']
# item['fifth_filename'] = fifth_filename
item['fifth_id'] = fifth_id[i]
# 河北省 石家庄市 长安区 建北街道办事处 棉一社区居民委员会 130102001001
item['fifth_titles'] = m[:m.rfind(" ")]+" "+fifth_titles[i]+" "+fifth_id[i]
items.append(item)
yield item
5.处理pipelines管道文件保存数据,可将结果保存到文件中(pipelines.py)
# -*- coding: utf-8 -*-
import json
from openpyxl import Workbook
# 转码操作,继承json.JSONEncoder的子类
class MyEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, bytes):
return str(o, encoding='utf-8')
return json.JSONEncoder.default(self, o)
# 处理数据,将数据保存在本地的excel表中
class StatsPipeline(object):
def __init__(self):
# self.filename = open("stats.csv", "w", encoding="utf-8")
self.wb = Workbook()
self.ws = self.wb.active
# 创建表头
self.ws.append(['third_titles','third_id','fourth_titles','fourth_id','fifth_titles','fifth_id'])
def process_item(self, item, spider):
# text = json.dumps((dict(item)), ensure_ascii=False, cls=MyEncoder) + '\n'
# self.filename.write(text)
# 只取六项,可以结合情况修改,保存到excel表中
text = [item['third_titles'],item['third_id'],item['fourth_titles'],item['fourth_id'],item['fifth_titles'],item['fifth_id']]
self.ws.append(text)
return item
def close_spider(self, spider):
self.wb.save('stats.xlsx')
print("数据处理完毕,谢谢使用!")
6.配置settings文件(settings.py)
# Obey robots.txt rules,具体含义参照:https://www.cdsy.xyz/computer/programme/Python/241210/cd64912.html
ROBOTSTXT_OBEY = False
# Override the default request headers:添加User-Agent信息
DEFAULT_REQUEST_HEADERS = {
'User-Agent': 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0);',
# 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
# 'Accept-Language': 'en',
}
# Configure item pipelines去掉下面注释,打开管道文件
ITEM_PIPELINES = {
'Stats.pipelines.StatsPipeline': 300,
}
# 还可以将日志存到本地文件中(可选添加设置)
LOG_FILE = "stats.log"
LOG_LEVEL = "DEBUG"
# 包含打印信息也一起写进日志里
LOG_STDOUT = True
7.以上设置完毕,进行爬取:执行项目命令crawl,启动Spider:
scrapy crawl stats
效果图: