python爬虫爬取民政局网中的行政区划带数据 并存入mysql

需求

爬取民政局网中的行政区划带数据:行政区名&代号
存入mysql

流程

  1. 从一级页面中提取进入二级页面的链接
    xpath表达式为://table/tr[2]//a/@href(此处需要在源码中写xpath表达式,页面节点中查看的是错误的)

注意:这个网站访问进去有个反爬虫操作:二级页面重定向到了其他页面(真正存放数据的页面),导致你从一级页面获取的二级页面链接的错误的,还需从重定向前的二级页面中获取重定向后页面的链接,然后去这个页面抓取数据

详细代码

先在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()

你可能感兴趣的:(spider,#python)