Python(爬虫篇)--- 动态HTML处理【一】AJAX数据获取


一、爬虫和反爬虫

关于爬虫(spider)和反爬虫(Anti-spider)以及反反爬虫(Anti-Anti-spider)之间的斗争相信大家也都了解,那个经典的小莫小黎爬虫大战大家也都有所耳闻。这里就不赘述了。

通常情况下,在爬虫与反爬虫的对弈中,爬虫一定会胜利。

换言之,只要人类能够正常访问的网页,爬虫在具备同等资源的情况下就一定可以抓取到。

关于爬虫部分一些建议:

  • 尽量减少请求次数,能抓列表页就不抓详情页,减轻服务器压力,程序员都是混口饭吃不容易。T_T
  • 不要只看 Web 网站,还有手机 App 和 H5,这样的反爬虫措施一般比较少。

实际应用时候,一般防守方做到根据 IP 限制频次就结束了,除非很核心的数据,不会再进行更多的验证,毕竟成本的问题会考虑到。如果真的对性能要求很高,可以考虑多线程 (一些成熟的框架如 Scrapy 都已支持),甚至分布式…

二、Ajax 数据获取

(一)什么是 Ajax

我们与网站服务器通信的唯一方式,就是发出 http 请求获取新页面。如果提交表单之 后,或从服务器获取信息之后,网站的页面不需要重新刷新,那么你访问的网站就在用 Ajax 技术。

Ajax 其实并不是一门语言,而是用来完成网络任务(可以认为它与网络数据采集差不多) 的一系列技术。Ajax 全称是 Asynchronous JavaScript and XML(异步 JavaScript 和 XML), 网站不需要使用单独的页面请求就可以和网络服务器进行交互(收发信息)。

Ajax 工作原理如图所示,Ajax 是一种客户端技术,当浏览器通过一些 javascript 动作(滑 动页面、点击一个按钮等等),此时 Ajax 引擎就会发送 HTTP 请求,服务器返回数据会交给 Ajax 引擎,最后 Ajax 引擎会将服务器返回的数据渲染到浏览器当前的页面中,这样我们不 需要整个页面进行刷新就能加载我们想要的内容。所以有时候,当我们需要爬取页面数据时, 如果能够找到 Ajax 引擎所发送的 HTTP 请求,我们就能直接通过发送这个请求从而获取数 据,这样可以大大提高我们爬取数据的效率。

在这里插入图片描述

(二)Ajax 请求分析方法

浏览器的开发者模式是有过滤 Ajax 请求功能的。Ajax 过滤界面如图所示,图中的 XHR 就是XmlHttpRequest 的缩写,这个对象就是 Ajax 技术的核心对象,Ajax 的功能都是 由它来实现的。

在这里插入图片描述
Ajax 请求的分析步骤可以总结为三步:

  1. 分析请求
    分析请求的目的是先找到这个页面中哪些请求是 Ajax 请求。当我们打开开发者工具, 选择 XHR 时,在该界面中出现的请求就是 Ajax 请求。比如豆瓣电影网站分析截图如图所示。
    在这里插入图片描述

  2. 分析响应
    分析响应的目的是从找到的 Ajax 请求中确定哪一条请求是获取页面数据的 Ajax 请求。 分析方法也很简单,当我们点击一个请求后,如上图所示,会在右边出现该请求的详细信息。其中 response 和 preview 都可以看到该请求响应的内容,如果响应内容中包含页面数据, 那么该请求就是我们想要获取的请求。
    豆瓣电影页面的 Ajax 请求响应如下图所示。
    在这里插入图片描述

  3. 解析响应内容
    一般Ajax请求返回的响应内容都是json数据,所以我们可以结合之前提到的的json 数据解析方法来将我们想要的内容从json 数据中提取出来。

三、案例:豆瓣电影信息爬取

  • 需求:
    获取所有分类下,所有电影信息(排名、电影名、演员、评分等)

  • 思路:
    1、 获取每一个分类的URL —> 获取电影的类型的id
    2.、先拼接获取总共电影数量的URL —> 请求,获取当前分类下的电影数量
    3.、最后拼接获取电影信息的URL —> 请求,获取数据

  • 分析:
    获取电影信息的ajax_url
    第一页:https://movie.douban.com/j/chart/top_listtype=24&interval_id=100%3A90&action=&start=0&limit=20
    第二页:https://movie.douban.com/j/chart/top_listtype=24&interval_id=100%3A90&action=&start=20&limit=20
    第三页:https://movie.douban.com/j/chart/top_listtype=24&interval_id=100%3A90&action=&start=40&limit=20
    第四页:https://movie.douban.com/j/chart/top_listtype=24&interval_id=100%3A90&action=&start=60&limit=20
    找到规律:每次start值都加20

    获取总共电影数量URL:
    https://movie.douban.com/j/chart/top_list_count?type=24&interval_id=100%3A90
    总计的页码:total//20+1

  • 具体代码

主函数:

if __name__ == '__main__':
    # 定义全局变量
    flag = 0
    # 定义基础的url
    base_url = 'https://movie.douban.com/chart'
    # 定义请求头
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36',
    }
    # 定义获取电影数据的URL
    movie_url = 'https://movie.douban.com/j/chart/top_list?type={}&interval_id=100%3A90&action=&start={}&limit=20'
    # 定义获取总共电影数的URL
    total_url = 'https://movie.douban.com/j/chart/top_list_count?type={}&interval_id=100%3A90'
    # 制定匹配type_id的正则表达式规则
    type_id_pattern = re.compile(r'type=(.*?)&')
    get_type(base_url)

定义请求函数

def get_requests(url):
    response = requests.get(url=url,headers=headers)
    if flag == 0:
        return etree.HTML(response.text)
    return response.json()

定义获取分类URL的函数

def get_type(url):
    # 发起请求,接收响应
    type_html = get_requests(url)
    # 将字符串转换成html元素对象
    # type_html = etree.HTML(response.text)
    # 提取数据
    type_list = type_html.xpath('//div[@class="types"]/span/a')
    for type in type_list:
        # 获取URL
        type_href = type.xpath('./@href')[0]
        # 获取分类名
        type_name = type.xpath('./text()')[0]
        type_id = type_id_pattern.findall(type_href)[0]
        get_total(type_id)

定义获取电影总共数量的函数

def get_total(type_id):
    global flag
    flag = 1
    # 请求
    # response = requests.get(url=total_url.format(type_id),headers=headers)
    response = get_requests(total_url.format(type_id))
    # 获取total
    total = response['total']
    get_movie(type_id,total)

定义获取电影信息的函数

def get_movie(type_id,total):
    for i in range(0,math.ceil(total/20)):
        # 发起请求
        data_list = get_requests(movie_url.format(type_id,i*20))
        # data_list = response.json()
        for data in data_list:
            dic = {}
            # 获取排名
            rank = data['rank']
            # 获取电影名
            movie = data['title']
            # 获取主演
            actor = ';'.join(data['actors'])
            # 获取上映时间
            release_time = data['release_date']
            # 获取评分
            score = data['score']

            dic['排名'] = rank
            dic['电影名'] = movie
            dic['主演'] = actor
            dic['上映时间'] = release_time
            dic['评分'] = score
            # 保存数据
            write_to_txt(dic)
            print(rank,movie,actor,release_time,score)

定义保存数据函数

def write_to_txt(dic):
    # 保存数据
    with open('豆瓣电影.txt','a',encoding='utf-8') as fp:
        fp.write(str(dic)+'\n')
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值