最近新学了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:mysql_2018@127.0.0.1:3306/taskdb",
"projectdb": "mysql+projectdb://root:mysql_2018@127.0.0.1:3306/projectdb",
"resultdb": "mysql+resultdb://root:mysql_2018@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;(不会的请百度python安装模块)
2、新建3个mysql的库,为了防止新建的库不是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'
CREATE TABLE `zones` (
`CodeNo` varchar(20) NOT NULL default '',
`ZoneName` varchar(40) NOT NULL default '',
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
3、百度学习一下CSS选择器的基本用法(# . : a nth-child),这是解析网页的基本功哦。
4、由于下钻后的网页是使用的相对网址,故需要拼接成一个绝对网址:
from urllib.parse import urljoin
urljoin(self.base_url, 遍历的相对url)
二、开始爬虫
pyspider的用法,百度一下就很多,这里不再详述,可以参考这个博客:
https://www.jianshu.com/p/1f166f320c66 。
在新建好的project后,系统会自动生成一个简单的爬虫结构(起始页->index页->detail页)。一开始我就被这个结构框住了,限定了思维。其实,我们完全可以不用管这个结构,主要利用这个调用结果来遍历网址。其他就当一般的python语句来编写了。
for each in response.doc('.citytr').items():
self.crawl()
注意:行政区域的网页不像一般例子,不存在detail页,因为每一次下钻网页返回的都是table格式的数据,不是返回的明细网页。我一开始就是这样去套这结构,结果就是浪费一些时间。
三、直接上代码
cursor.close()
self.db.close()
注意:这两个地方的语句是我运行完结果后加上去的,为了结束后关闭连接。但是我没有运行测试,如果报错,请删除后运行或者自行修改。
如果还要爬取到最末一级村委会,可以在下边的语句增加一个页面方法即可,具体怎么加?只能劳烦自己举一反三了。
#!/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:mysql_2018@127.0.0.1:3306/taskdb",
"projectdb": "mysql+projectdb://root:mysql_2018@127.0.0.1:3306/projectdb",
"resultdb": "mysql+resultdb://root:mysql_2018@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/2016/53.html'
self.db = pymysql.connect(host="localhost",user="root",
password="mysql_2018",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)
四、查看结果
Mysql的查询结果是不会自动返回行号的,得手工添加一个变量:
SELECT @rowno:=@rowno+1 as rowno,r.* from zones r,(select @rowno:=0) t