侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

python爬取Ajax动态加载网页过程解析

Python  /  管理员 发布于 7年前   301

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

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":"1":"2",

3、点击 Replace All

民政部网站数据抓取

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

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

实现步骤

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

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

import requestsfrom lxml import etreeimport 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).textparse_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="https:/article/(.*?)" rel="external nofollow" rel="external nofollow" ', two_html, re.S)[0]

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

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

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

cursor.execute('select * from version')result = self.cursor.fetchall()if result:  if result[-1][0] == two_link:    print('已是最新')  else:    # 有更新,开始抓取    # 将链接再重新插入version表记录

4、代码实现

import requestsfrom lxml import etreeimport reimport pymysqlclass 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="https:/article/(.*?)" 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-->Query String Parameters(查询参数)

豆瓣电影数据抓取案例

目标

地址: 豆瓣电影 - 排行榜 - 剧情

https://movie.douban.com/typerank?

type_name=%E5%89%A7%E6%83%85&type=11&interval_id=100:90&action=

目标: 爬取电影名称、电影评分

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地址+查询参数

'https://movie.douban.com/j/chart/top_list?'+'type=11&interval_id=100%3A90&action=&start=20&limit=20'

代码实现

import requestsimport timefrom fake_useragent import UserAgentclass 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地址及目标

URL: 百度搜索腾讯招聘 - 查看工作岗位 https://careers.tencent.com/search.html

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

要求与分析

通过查看网页源码,得知所需数据均为 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)',]
import timeimport jsonimport randomimport requestsfrom useragents import ua_listclass 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))

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


  • 上一条:
    python爬虫 猫眼电影和电影天堂数据csv和mysql存储过程解析
    下一条:
    python实现静态服务器
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • 在python语言中Flask框架的学习及简单功能示例(0个评论)
    • 在Python语言中实现GUI全屏倒计时代码示例(0个评论)
    • Python + zipfile库实现zip文件解压自动化脚本示例(0个评论)
    • python爬虫BeautifulSoup快速抓取网站图片(1个评论)
    • vscode 配置 python3开发环境的方法(0个评论)
    • 近期文章
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • Laravel从Accel获得5700万美元A轮融资(0个评论)
    • 在go + gin中gorm实现指定搜索/区间搜索分页列表功能接口实例(0个评论)
    • 在go语言中实现IP/CIDR的ip和netmask互转及IP段形式互转及ip是否存在IP/CIDR(0个评论)
    • PHP 8.4 Alpha 1现已发布!(0个评论)
    • Laravel 11.15版本发布 - Eloquent Builder中添加的泛型(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-10
    • 2016-11
    • 2018-04
    • 2020-03
    • 2020-04
    • 2020-05
    • 2020-06
    • 2022-01
    • 2023-07
    • 2023-10
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客