省->市(州)->县(区)->乡镇(办事处)->村委会(社区)
云南统计局查行政区域的网站方法:
国家统计局的网站基本没有反爬虫,挺适合我们新手操作哦!
正式开始编写爬虫的时候还是百度了半天的,中间也走过一点弯路的,其中在把结果写入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
按照如下代码新建的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:[email protected]:3306/taskdb",
"projectdb": "mysql+projectdb://root:[email protected]:3306/projectdb",
"resultdb": "mysql+resultdb://root:[email protected]: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"
}
简述完了,开始我们的爬虫之旅吧!
我安装了Anaconda,我电脑的路径是:
E:\Users\hawk\Anaconda3\Scripts
通过cmd,切换到上边的路径(不会切换? 请百度cmd cd):
pip install pyspider
pip install pymysql
如果还不会的请百度python怎么安装模块。
为了防止新建的库不是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;
这个涉及到的东西多了,只需百度一下,就会有很多教程,在此略过。
百度学习一下CSS选择器的基本用法:(# . : a nth-child,各种元素的选取操作),这是解析网页的基本功哦。
由于下钻后的网页是使用的相对网址,故需要拼接成一个绝对网址:
from urllib.parse import urljoin
urljoin(self.base_url, 遍历的相对url)
在E:\Users\hawk\Anaconda3\Scripts路径下(你的路径请自查):
pyspider all
建好一个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:[email protected]:3306/taskdb",
"projectdb": "mysql+projectdb://root:[email protected]:3306/projectdb",
"resultdb": "mysql+resultdb://root:[email protected]: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操作,把乡镇列关联上县、市。
这样,就大功告成了。
朋友,你成功了吗?