用python3的pyspider爬取国家统计局的行政区域(只到乡镇级,更新到2017年)


刚开始学习爬虫,学了pyspider,就想练练手。
想到不久前需要一些云南地区的行政数据,还是在网络百度半天才下载的。
现在既然会爬虫了,那就自己动手,随时可以用相对新的数据了(最近发现统计更新到2017年了,我也从新爬取一下,顺便把几个地方补充下详细操作点)。
行政划分的总体结构是:

省->市(州)->县(区)->乡镇(办事处)->村委会(社区)

云南统计局查行政区域的网站方法:
在这里插入图片描述

国家统计局的网站基本没有反爬虫,挺适合我们新手操作哦!

正式开始编写爬虫的时候还是百度了半天的,中间也走过一点弯路的,其中在把结果写入mysql的过程中有地方会报错:

一、爬虫遇到的问题

1、Mysql中文乱码问题:

解决办法就是修改my.ini里的内容(my.ini还是在安装路径下搜索吧),具体添加部分如下:

[client]
default-character-set=utf8
# pipe=

# socket=MYSQL

port=3306

[mysql]
no-beep
default-character-set=utf8
# default-character-set=


# SERVER SECTION
# ----------------------------------------------------------------------
#
# The following options will be read by the MySQL Server. Make sure that
# you have installed the server correctly (see above) so it reads this 
# file.
#
# server_type=3
[mysqld]
character-set-server=utf8

2、pyspider的config设置问题

按照如下代码新建的config.json文件,同时把文件放在了pyspider(安装好的库文件包)的文件夹下,但是运行pyspider -c config.json all 还是报错找不到路径。
解决办法:在cmd语句切换到pyspider的路径后,再用pyspider -c config.json all。
备注:pyspider的路径…\Lib\site-packages\pyspider
如果嫌麻烦,也可以直接把代码放在crawl_config = {}里边了。

{
        "taskdb": "mysql+taskdb://root:yourpassword@127.0.0.1:3306/taskdb",
        "projectdb": "mysql+projectdb://root:yourpassword@127.0.0.1:3306/projectdb",
        "resultdb": "mysql+resultdb://root:yourpassword@127.0.0.1:3306/resultdb",
        "message_queue": "redis://127.0.0.1:6379/db",
        "phantomjs-proxy": "127.0.0.1:25555",
        "scheduler" : {
        "xmlrpc-host": "0.0.0.0",
        "delete-time": 3600
         },
         "webui": {
         "port": 5000,
         "username": "hawk",
         "password": "mima",
         "need-auth": "true"
         }

简述完了,开始我们的爬虫之旅吧!

二、准备工作

1、安装pyspider、pymysql

我安装了Anaconda,我电脑的路径是:

E:\Users\hawk\Anaconda3\Scripts

通过cmd,切换到上边的路径(不会切换? 请百度cmd cd):

pip install pyspider
pip install pymysql

如果还不会的请百度python怎么安装模块。

2、新建3个mysql的库及1个结果表

为了防止新建的库不是utf编码的,我们还是用如下语句创建吧。

CREATE DATABASE `taskdb`  
CHARACTER SET 'utf8'  
COLLATE 'utf8_general_ci'

CREATE DATABASE `projectdb`  
CHARACTER SET 'utf8'  
COLLATE 'utf8_general_ci'

CREATE DATABASE `resultdb`  
CHARACTER SET 'utf8'  
COLLATE 'utf8_general_ci'

use resultdb;
CREATE TABLE `zones` (  
`CodeNo` varchar(20) NOT NULL default '',  
`ZoneName` varchar(40) NOT NULL default '',  
) ENGINE=InnoDB DEFAULT CHARSET=utf8;  

3、安装Mysql

这个涉及到的东西多了,只需百度一下,就会有很多教程,在此略过。

4、学习CSS选择器的基本用法

百度学习一下CSS选择器的基本用法:(# . : a nth-child,各种元素的选取操作),这是解析网页的基本功哦。

5、拼接url

由于下钻后的网页是使用的相对网址,故需要拼接成一个绝对网址:

from urllib.parse import urljoin
urljoin(self.base_url, 遍历的相对url)

三、开始爬虫

1、启动pyspider

在E:\Users\hawk\Anaconda3\Scripts路径下(你的路径请自查):

pyspider all

2、新建一个project

建好一个project,系统会自动生成一个简单的爬虫结构(起始页->index页->detail页)。
pyspider的具体用法,百度一下就很多,这里不再详述,可以参考一下这个博客:https://www.jianshu.com/p/1f166f320c66 ,至于博客中的安装方法,你就不要去运行源码了,只要你切换好了安装路径,用pip 安装肯定没问题 。
一开始我就被这个结构框住了,限定了思维。其实,我们可以不用硬套这个结构,主要利用这个调用方法的结果来遍历网址。其他就当一般的python语句来编写了。

for each in response.doc('.citytr').items():
    self.crawl()

注意:行政区域的网页不像一般例子,不存在detail页,因为每一次下钻网页返回的都是table格式的数据,不是返回的明细网页。我一开始就是这样去套这结构,结果就是浪费一些时间。

四、上详细代码

我只爬取到乡镇级,如果还要爬取到最末一级村委会,可以在下边的语句增加一个页面方法即可,具体怎么加?只能劳烦您举一反三了。

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# Created on 2018-06-01 09:28:18
# Project: zones_yunnan

from pyspider.libs.base_handler import *
from urllib.parse import urljoin
import pymysql

class Handler(BaseHandler):
    #headers\cookies亦可以放入起始设置中
    crawl_config = {
        "taskdb": "mysql+taskdb://root:yourpassword@127.0.0.1:3306/taskdb",
        "projectdb": "mysql+projectdb://root:yourpassword@127.0.0.1:3306/projectdb",
        "resultdb": "mysql+resultdb://root:yourpassword@127.0.0.1:3306/resultdb",
        "message_queue": "redis://127.0.0.1:6379/db",
        "phantomjs-proxy": "127.0.0.1:25555",
        "scheduler" : {
        "xmlrpc-host": "0.0.0.0",
        "delete-time": 3600
         },
         "webui": {
         "port": 5000,
         "username": "hawk",
         "password": "mima",
         "need-auth": "true"
         }
    }
    
    
    #初始化基础页url
    def __init__(self):
        self.base_url = 'http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2017/53.html'
        self.db = pymysql.connect(host="localhost",user="root", 
    password="yourpassword",db="resultdb",port=3306,charset='utf8') 
        
    #定义一个mysql的insert方法,方便后边写码
    def insert_zone(self, code, zone_name):
        try:
            cursor = self.db.cursor()
            sql_insert = 'insert into zones(CodeNo,ZoneName) values ("%s","%s")' % (code, zone_name);
            cursor.execute(sql_insert)
            self.db.commit()
        except Exception as e:
            print(e)
            self.db.rollback() 
            cursor.close()
            self.db.close()
    
    
    #crawl的参数:priority值越大越优先;fetch_type='js'即可以js渲染;还有use_agent\headers\cookies\load_images.
    @every(minutes=24 * 60)
    def on_start(self):
        self.crawl(self.base_url, callback=self.city_page)
    
    @config(age=10 * 24 * 60 * 60)
    def city_page(self, response):
        for each in response.doc('.citytr').items():
            self.crawl(urljoin(self.base_url, each.find('td a:last-child').attr('href')), callback=self.county_page)
            code=each.find('td:first-child').text()
            city=each.find('td:last-child').text()
            self.insert_zone(code,city)
                     

    @config(age=10 * 24 * 60 * 60)
    def county_page(self, response):
        for each in response.doc('.countytr').items():
            self.crawl(urljoin(self.base_url, each.find('td a:last-child').attr('href')), callback=self.town_page)
            code=each.find('td:first-child').text()
            county=each.find('td:last-child').text()
            self.insert_zone(code,county)

    @config(age=10 * 24 * 60 * 60,priority=2)
    def town_page(self, response):
        baseurl = 'http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2016/'
        for each in response.doc('.towntr').items():
            #self.crawl(urljoin(baseurl ,each.find('td a:last-child').attr('href')), #callback=self.town_page)
            code=each.find('td:first-child').text()
            town=each.find('td:last-child').text()
            self.insert_zone(code,town)
     

五、运行工程

保存好代码后,点击左上角的Pyspider,就会回到工程主界面了。
按照下图中标红色的地方,把Status的状态改成running,然后就点击右边的Run。 嘿嘿,一切都顺利跑起来了!!!
就等着程序自己跑完位置。
在这里插入图片描述

六、查看结果

登录Mysql,通过查询Zones表格数据,就可以查看到所有爬取到的结果了。
Mysql的查询结果是不会自动返回行号的,得手工添加一个变量:

SELECT @rowno:=@rowno+1 as rowno,r.* from zones r,(select @rowno:=0) t

这里写图片描述

七、整理结果

爬取出来的结果是一个一维表结构,怎么方便查询市、县、乡镇的直观关系呢?这就需要用到CodeNo了。
仔细观察这CodeNo,发现这样一个规律:前4位数代表的是市或州、前6位代表的是县及区、前9位代表的是乡、镇、办事处。
通过这关系,在excel中处理下,用mid函数提取前4位、5-6位、7-9位字符。
当5-6位的字符为00的,就是市或州;
当7-9位的字符为000,同时5-6位的字符不为00的,就是县或区;
当7-9位的字符不为000的,就是乡、镇、办事处的;
通过vlookup操作,把乡镇列关联上县、市。
在这里插入图片描述
这样,就大功告成了。
朋友,你成功了吗?

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值