爬取民政局网中的行政区划带数据:行政区名&代号
存入mysql
注意:这个网站访问进去有个反爬虫操作:二级页面重定向到了其他页面(真正存放数据的页面),导致你从一级页面获取的二级页面链接的错误的,还需从重定向前的二级页面中获取重定向后页面的链接,然后去这个页面抓取数据
先在mysql中创建一个库govdb,再在里面创建四个表:
create database govdb charset utf8;
use govdb;
create table province(
p_name varchar(20),
p_code varchar(20)
)charset=utf8;
create table city(
c_name varchar(20),
c_code varchar(20),
c_father_code varchar(20)
)charset=utf8;
create table county(
x_name varchar(20),
x_code varchar(20),
x_father_code varchar(20)
)charset=utf8;
#指纹表
create table request_finger(
finger char(32)
)charset=utf8;
python代码
import requests
from lxml import etree
import re
from hashlib import md5
import pymysql
class MzbSpider:
def __init__(self):
#明政局一级页面链接
self.url = 'http://www.mca.gov.cn/article/sj/xzqh/2019/'
self.headers = {'User-Agent':'xxxxxxxxx'}
#连接mysql
self.con = pymysql.connect('localhost','root','123456','govdb',charset='utf8')
#创建游标
self.cursor = self.con.cursor()
#创建3个列表,用于executemany(),executemany()执行的数据格式为 [(),(),...]
self.province = []
self.city = []
self.county = []
def get_false_link(self):
#获取一级页面响应
html = requests.get(url=self.url,headers=self.headers).text
#提取跳转前的二级页面链接
p = etree.HTML(html)
#在页面元素中查看详细的二级页面链接并拼接
link = 'http://www.mca.gov.cn' + p.xpath('//table/tr[2]//a/@href')[0]
print(link) #打印出重定向前的二级页面链接
#对link进行加密
s = md5()
s.update(lin.encode())
finger = s.hexdigest()
sel = 'select finger from request_finger where finger=%s'
#execute()返回值为修改的条数
result = self.cursor.execute(sel,[finger])
#如果不在表中,说明有更新,去抓数据,然后存入数据库(每次抓取都是完全抓一遍,原数据库中的数据需要清空,避免重复)
If not result:
self.get_real_link(link)
#抓后将指纹存到指纹表
ins = 'insert into request_finger values(%s)'
self.cursor.execute(ins,[finger])
self.db.commit()
else:
print('数据已是最新')
#向二级页面发请求获取数据
self.get_real_link(link)
def get_real_link(self,link):
html = requests.get(url=link,headers=self.headers).text
#将跳转前的二级页面返回的html写入一个文件中,并在里面找重定向的那个链接,用re匹配出来
with open('gov.html','w') as f:
f.write(html)
# re匹配,返回的数据类型为列表,切出字符串
p = re.compile('window.location.href="(.*?)"',re.S)
real_link = p.findall(html)[0]
#从跳转后的二级页面匹配出最终数据:行政区划代码
self.get_data(real_link)
#从重定向后的二级页面提取行政区划带代码
def get_data(self,real_link):
html = requests.get(url=real_link,headers=self.headers).text
p = etree.HTML(html)
#根据页面节点写xpath表达式://tr[@height="19"]
tr_list = p.xpath('//tr[@height="19"]') #返回的是节点对象
#查看网页节点对象
#发现tr节点下的第三个td子节点是行政区号(xpath表达式是:./td[2].text()),第二个td节点是区名(xpath表达式:./td[2].text())
for tr in tr_list:
name = tr.xpath('./td[3]/text()')[0].strip()
code = tr.xpath('./td[2]/text()')[0].strip()
print(name,code)
#以上省市县数据全都抓下来了,现在要将数据分门别类放到初始化的列表中去
#如果后四位是0,则是省,放到省表中去,有四个直辖市(既是省也是市)得同步添加到市表中去
if code[-4:] == '0000':
self.province.append((name,code)]
if code[:2} in ['11','12','31','50']: #四个直辖市的code开头分别为11,12,31,50
cfcode = code[:2] = '0000'
self.city.append((name,code,cfcode))
#如果后两位是0,则肯定是市
elif code[-2:] == '00':
#拼出该市所属省的code
cfcode = code[:2] + '0000'
self.city.append((name,code,cfcode))
#用一个变量记录现在正在遍历的市,因为遍历完这个市之后一定遍历他下面的县
last_city = code
#县
else:
#四个直辖市有点特殊,县的xfcode得单独拼
if code[:2] in ['11','12','31','50']:
xfcode = code[:2] + '0000'
else:
#将上面记录的市,直接给到县需要的数据中
xfcode = last_city
self.county.append((name,code,xfcode))
#将准备好的数据存入数据库
self.insert_mysql()
#将数据存入数据库的具体函数
def insert_mysql(self):
#先把数据库中旧的数据清空
del1 = 'delete from province'
del2 = 'delete from city'
del3 = 'delete from county'
self.cursor.execute(del1)
self.cursor.execute(del2)
self.cursor.execute(del3)
self.db.commit()
#再插入新抓的数据
ins1 = 'Insert into province values(%s,%s)'
ins2 = 'insert into city values(%s,%s,%s)'
ins3 = 'insert into county values(%s,%s,%s)'
self.cursor.executemany(ins1,self.province)
self.cursor.executemany(ins2,self.city)
self.cursor.executemany(ins3,self.county)
self.db.commit()
#入口函数
def run(self):
self.get_false_link()
if __name__ == '__main__':
spider = MzbSpider()
spider.run()