python爬取ajax异步加载网页_爬虫——爬取Ajax动态加载网页

常见的反爬机制及处理方式

1、Headers反爬虫 :Cookie、Referer、User-Agent

解决方案: 通过F12获取headers,传给requests.get()方法

2、IP限制 :网站根据IP地址访问频率进行反爬,短时间内进制IP访问

解决方案:

1、构造自己IP代理池,每次访问随机选择代理,经常更新代理池

2、购买开放代理或私密代理IP

3、降低爬取的速度

3、User-Agent限制 :类似于IP限制

解决方案: 构造自己的User-Agent池,每次访问随机选择

5、对查询参数或Form表单数据认证(salt、sign)

解决方案: 找到JS文件,分析JS处理方法,用Python按同样方式处理

6、对响应内容做处理

解决方案: 打印并查看响应内容,用xpath或正则做处理

python中正则处理headers和formdata

1、pycharm进入方法 :Ctrl + r ,选中 Regex

2、处理headers和formdata

(.*): (.*)

"\$1": "\$2",

3、点击 Replace All

民政部网站数据抓取

目标: 抓取最新中华人民共和国县以上行政区划代码

URL: http://www.mca.gov.cn/article/sj/xzqh/2019/ - 民政数据 - 行政区划代码

实现步骤

1、从民政数据网站中提取最新行政区划代码链接

最新的在上面,命名格式: 2019年X月中华人民共和国县以上行政区划代码

importrequestsfrom lxml importetreeimportre

url= 'http://www.mca.gov.cn/article/sj/xzqh/2019/'headers= {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36'}

html= requests.get(url, headers=headers).text

parse_html=etree.HTML(html)

article_list= parse_html.xpath('//a[@class="artitlelist"]')

​for article inarticle_list:

title= article.xpath('./@title')[0]#正则匹配title中包含这个字符串的链接

if title.endswith('代码'):#获取到第1个就停止即可,第1个永远是最新的链接

two_link = 'http://www.mca.gov.cn' + article.xpath('./@href')[0]print(two_link)break

2、从二级页面链接中提取真实链接(反爬-响应网页内容中嵌入JS,指向新的网页链接)

向二级页面链接发请求得到响应内容,并查看嵌入的JS代码

正则提取真实的二级页面链接

# 爬取二级“假”链接

two_html = requests.get(two_link, headers=headers).text#从二级页面的响应中提取真实的链接(此处为JS动态加载跳转的地址)

new_two_link = re.findall(r'window.location.href="(.*?)"', two_html, re.S)[0]

3、在数据库表中查询此条链接是否已经爬取,建立增量爬虫

数据库中建立version表,存储爬取的链接

每次执行程序和version表中记录核对,查看是否已经爬取过

cursor.execute('select * from version')

result=self.cursor.fetchall()ifresult:if result[-1][0] ==two_link:print('已是最新')else:#有更新,开始抓取

#将链接再重新插入version表记录

4、代码实现

importrequestsfrom lxml importetreeimportreimportpymysqlclassGovementSpider(object):def __init__(self):

self.url= 'http://www.mca.gov.cn/article/sj/xzqh/2019/'self.headers= {'User-Agent': 'Mozilla/5.0'}#创建2个对象

self.db = pymysql.connect('127.0.0.1', 'root', '123456', 'govdb', charset='utf8')

self.cursor=self.db.cursor()#获取假链接

defget_false_link(self):

html= requests.get(url=self.url, headers=self.headers).text#此处隐藏了真实的二级页面的url链接,真实的在假的响应网页中,通过js脚本生成,

#假的链接在网页中可以访问,但是爬取到的内容却不是我们想要的

parse_html =etree.HTML(html)

a_list= parse_html.xpath('//a[@class="artitlelist"]')for a ina_list:#get()方法:获取某个属性的值

title = a.get('title')if title.endswith('代码'):#获取到第1个就停止即可,第1个永远是最新的链接

false_link = 'http://www.mca.gov.cn' + a.get('href')print("二级“假”链接的网址为", false_link)break

#提取真链接

self.incr_spider(false_link)#增量爬取函数

defincr_spider(self, false_link):

self.cursor.execute('select url from version where url=%s', [false_link])#fetchall: (('http://xxxx.html',),)

result =self.cursor.fetchall()#not result:代表数据库version表中无数据

if notresult:

self.get_true_link(false_link)#可选操作: 数据库version表中只保留最新1条数据

self.cursor.execute("delete from version")#把爬取后的url插入到version表中

self.cursor.execute('insert into version values(%s)', [false_link])

self.db.commit()else:print('数据已是最新,无须爬取')#获取真链接

defget_true_link(self, false_link):#先获取假链接的响应,然后根据响应获取真链接

html = requests.get(url=false_link, headers=self.headers).text#从二级页面的响应中提取真实的链接(此处为JS动态加载跳转的地址)

re_bds = r'window.location.href="(.*?)"'pattern=re.compile(re_bds, re.S)

true_link=pattern.findall(html)[0]

self.save_data(true_link)#提取真链接的数据

#用xpath直接提取数据

defsave_data(self, true_link):

html= requests.get(url=true_link, headers=self.headers).text#基准xpath,提取每个信息的节点列表对象

parse_html =etree.HTML(html)

tr_list= parse_html.xpath('//tr[@height="19"]')for tr intr_list:

code= tr.xpath('./td[2]/text()')[0].strip() #行政区划代码

name = tr.xpath('./td[3]/text()')[0].strip() #单位名称

print(name, code)#主函数

defmain(self):

self.get_false_link()if __name__ == '__main__':

spider=GovementSpider()

spider.main()

动态加载数据抓取-Ajax

特点

右键 -> 查看网页源码中没有具体数据

滚动鼠标滑轮或其他动作时加载

抓取

F12打开控制台,选择XHR异步加载数据包,找到页面动作抓取网络数据包

通过XHR-->Header-->General-->Request URL,获取json文件URL地址

通过XHR-->Header-->Query String Parameters(查询参数)

豆瓣电影数据抓取案例

目标

F12抓包(XHR)

1、Request URL(基准URL地址) :https://movie.douban.com/j/chart/top_list?

2、Query String Paramaters(查询参数)

#查询参数如下:

type: 13 #电影类型

interval_id: 100:90action:'[{},{},{}]'start: 0#每次加载电影的起始索引值

limit: 20 #每次加载的电影数量

json文件在以下地址:

基准URL地址+查询参数

代码实现

importrequestsimporttimefrom fake_useragent importUserAgentclassDoubanSpider(object):def __init__(self):

self.base_url= 'https://movie.douban.com/j/chart/top_list?'self.i=0defget_html(self, params):

headers= {'User-Agent': UserAgent().random}

res= requests.get(url=self.base_url, params=params, headers=headers)

res.encoding= 'utf-8'html= res.json() #将json格式的字符串转为python数据类型

self.parse_html(html) #直接调用解析函数

defparse_html(self, html):#html: [{电影1信息},{电影2信息},{}]

item ={}for one inhtml:

item['name'] = one['title'] #电影名

item['score'] = one['score'] #评分

item['time'] = one['release_date'] #打印测试

#打印显示

print(item)

self.i+= 1

#获取电影总数

defget_total(self, typ):#异步动态加载的数据 都可以在XHR数据抓包

url = 'https://movie.douban.com/j/chart/top_list_count?type={}&interval_id=100%3A90'.format(typ)

ua=UserAgent()

html= requests.get(url=url, headers={'User-Agent': ua.random}).json()

total= html['total']returntotaldefmain(self):

typ= input('请输入电影类型(剧情|喜剧|动作):')

typ_dict= {'剧情': '11', '喜剧': '24', '动作': '5'}

typ=typ_dict[typ]

total= self.get_total(typ) #获取该类型电影总数量

for page in range(0, int(total), 20):

params={'type': typ,'interval_id': '100:90','action': '','start': str(page),'limit': '20'}

self.get_html(params)

time.sleep(1)print('爬取的电影的数量:', self.i)if __name__ == '__main__':

spider=DoubanSpider()

spider.main()

腾讯招聘数据抓取(Ajax)

确定URL地址及目标

目标: 职位名称、工作职责、岗位要求

要求与分析

通过查看网页源码,得知所需数据均为 Ajax 动态加载

通过F12抓取网络数据包,进行分析

一级页面抓取数据: 职位名称

二级页面抓取数据: 工作职责、岗位要求

一级页面json地址(pageIndex在变,timestamp未检查)

https://careers.tencent.com/tencentcareer/api/post/Query?timestamp=1563912271089&countryId=&cityId=&bgIds=&productId=&categoryId=&parentCategoryId=&attrId=&keyword=&pageIndex={}&pageSize=10&language=zh-cn&area=cn

二级页面地址(postId在变,在一级页面中可拿到)

https://careers.tencent.com/tencentcareer/api/post/ByPostId?timestamp=1563912374645&postId={}&language=zh-cn

useragents.py文件

ua_list =['Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.163 Safari/535.1','Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0) Gecko/20100101 Firefox/6.0','Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; InfoPath.3)',

]

importtimeimportjsonimportrandomimportrequestsfrom useragents importua_listclassTencentSpider(object):def __init__(self):

self.one_url= 'https://careers.tencent.com/tencentcareer/api/post/Query?timestamp=1563912271089&countryId=&cityId=&bgIds=&productId=&categoryId=&parentCategoryId=&attrId=&keyword=&pageIndex={}&pageSize=10&language=zh-cn&area=cn'self.two_url= 'https://careers.tencent.com/tencentcareer/api/post/ByPostId?timestamp=1563912374645&postId={}&language=zh-cn'self.f= open('tencent.json', 'a') #打开文件

self.item_list = [] #存放抓取的item字典数据

#获取响应内容函数

defget_page(self, url):

headers= {'User-Agent': random.choice(ua_list)}

html= requests.get(url=url, headers=headers).text

html= json.loads(html) #json格式字符串转为Python数据类型

returnhtml#主线函数: 获取所有数据

defparse_page(self, one_url):

html=self.get_page(one_url)

item={}for job in html['Data']['Posts']:

item['name'] = job['RecruitPostName'] #名称

post_id = job['PostId'] #postId,拿postid为了拼接二级页面地址

#拼接二级地址,获取职责和要求

two_url =self.two_url.format(post_id)

item['duty'], item['require'] =self.parse_two_page(two_url)print(item)

self.item_list.append(item)#添加到大列表中

#解析二级页面函数

defparse_two_page(self, two_url):

html=self.get_page(two_url)

duty= html['Data']['Responsibility'] #工作责任

duty = duty.replace('\r\n', '').replace('\n', '') #去掉换行

require = html['Data']['Requirement'] #工作要求

require = require.replace('\r\n', '').replace('\n', '') #去掉换行

returnduty, require#获取总页数

defget_numbers(self):

url= self.one_url.format(1)

html=self.get_page(url)

numbers= int(html['Data']['Count']) // 10 + 1 #每页有10个推荐

returnnumbersdefmain(self):

number=self.get_numbers()for page in range(1, 3):

one_url=self.one_url.format(page)

self.parse_page(one_url)#保存到本地json文件:json.dump

json.dump(self.item_list, self.f, ensure_ascii=False)

self.f.close()if __name__ == '__main__':

start=time.time()

spider=TencentSpider()

spider.main()

end=time.time()print('执行时间:%.2f' % (end - start))

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值