动态网页抓取(常见的反爬机制及处理方式(,文中))
优采云 发布时间: 2021-10-18 15:08动态网页抓取(常见的反爬机制及处理方式(,文中))
本文文章主要介绍python爬取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、查询参数或表单数据的认证(salt,sign)
解决方法:找到JS文件,分析JS处理方式,用Python同样处理
6、处理响应内容
解决方法:打印查看响应内容,使用xpath或者正则处理
python中标题和表单数据的常规处理
1、pycharm 进入方法:Ctrl + r,选择Regex
2、处理标头和表单数据
(.*): (.*)
"1":"1":"2",
3、点击全部替换
民政部网站数据采集
目标:抓取中华人民共和国县级以上最新行政区划代码
网址:-民政数据-行政部门代码
实施步骤
1、民政数据提取最新行政区划代码链接网站
最新在上,命名格式:X,2019,中华人民共和国县及以上行政区划代码
import requests from lxml import etree import re 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 in article_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="(.*?)" rel="external nofollow" rel="external nofollow" ', two_html, re.S)[0]
3、在数据库表中查询该链接是否被爬取过,构建增量爬虫
在数据库中创建一个版本表来存储爬取的链接
每次程序执行时都会记录版本表,查看是否被爬取过
cursor.execute('select * from version') result = self.cursor.fetchall() if result: if result[-1][0] == two_link: print('已是最新') else: # 有更新,开始抓取 # 将链接再重新插入version表记录
4、代码实现
import requests from lxml import etree import re import pymysql class GovementSpider(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() # 获取假链接 def get_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 in a_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) # 增量爬取函数 def incr_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 not result: 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('数据已是最新,无须爬取') # 获取真链接 def get_true_link(self, false_link): # 先获取假链接的响应,然后根据响应获取真链接 html = requests.get(url=false_link, headers=self.headers).text # 从二级页面的响应中提取真实的链接(此处为JS动态加载跳转的地址) re_bds = r'window.location.href="(.*?)" rel="external nofollow" rel="external nofollow" ' pattern = re.compile(re_bds, re.S) true_link = pattern.findall(html)[0] self.save_data(true_link) # 提取真链接的数据 # 用xpath直接提取数据 def save_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 in tr_list: code = tr.xpath('./td[2]/text()')[0].strip() # 行政区划代码 name = tr.xpath('./td[3]/text()')[0].strip() # 单位名称 print(name, code) # 主函数 def main(self): self.get_false_link() if __name__ == '__main__': spider = GovementSpider() spider.main()
动态加载数据捕获-Ajax
特征
右键->查看没有具体数据的网页源代码
滚动鼠标滚轮或其他动作时加载
抓住
F12 打开控制台,选择XHR异步加载数据包,找到抓取网络数据包的页面动作
通过XHR-->Header-->General-->Request URL获取json文件的URL地址
通过XHR-->Header-->查询字符串参数
豆瓣电影数据采集案例
目标
地址:豆瓣电影排行榜-故事
type_name=%E5%89%A7%E6%83%85&type=11&interval_id=100:90&action=
目标:抓取电影名称、电影评分
F12 数据包捕获 (XHR)
1、请求 URL(基本 URL 地址):
2、Query String Paramaters(查询参数)
# 查询参数如下: type: 13 # 电影类型 interval_id: 100:90 action: '[{},{},{}]' start: 0 # 每次加载电影的起始索引值 limit: 20 # 每次加载的电影数量
json文件位于以下地址:
基本 URL 地址 + 查询参数
''+'type=11&interval_id=100%3A90&action=&start=20&limit=20'
代码
import requests import time from fake_useragent import UserAgent class DoubanSpider(object): def __init__(self): self.base_url = 'https://movie.douban.com/j/chart/top_list?' self.i = 0 def get_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) # 直接调用解析函数 def parse_html(self, html): # html: [{电影1信息},{电影2信息},{}] item = {} for one in html: item['name'] = one['title'] # 电影名 item['score'] = one['score'] # 评分 item['time'] = one['release_date'] # 打印测试 # 打印显示 print(item) self.i += 1 # 获取电影总数 def get_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'] return total def main(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在变化,时间戳不检查)
{}&pageSize=10&language=zh-cn&area=cn
二级页面地址(postId在变化,可以在一级页面获取)
{}&language=zh-cn
用户代理.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)', ]
import time import json import random import requests from useragents import ua_list class TencentSpider(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字典数据 # 获取响应内容函数 def get_page(self, url): headers = {'User-Agent': random.choice(ua_list)} html = requests.get(url=url, headers=headers).text html = json.loads(html) # json格式字符串转为Python数据类型 return html # 主线函数: 获取所有数据 def parse_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) # 添加到大列表中 # 解析二级页面函数 def parse_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', '') # 去掉换行 return duty, require # 获取总页数 def get_numbers(self): url = self.one_url.format(1) html = self.get_page(url) numbers = int(html['Data']['Count']) // 10 + 1 # 每页有10个推荐 return numbers def main(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))
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持html中文网。
以上就是python爬取Ajax动态加载网页过程分析的详细内容。更多详情请关注其他相关html中文网站文章!