zoukankan      html  css  js  c++  java
  • Ajax异步数据抓取

    1.简介

     1 有时候我们在用requests抓取页面的时候,得到的结果可能和在浏览器中看到的不一样,在浏览
     2 器中可以看到正常显示的页面数据,但是使用requests得到的结果并没有。这是因为requests获取的
     3 都是原始的HTML文档,而浏览器中的页面则是经过JavaScript处理数据后生成的结果,这些数据的
     4 来源有多种,可能是通过ajax加载的,可能是包含在HTML文档中的,也可能是经过JavaScript和特
     5 定算法计算后生成的。
     6 对于第一种情况,数据加载是一种异步加载方式,原始的页面最初不会包含某些数据,原始页面
     7 加载完后,会再向服务器请求某个接口获取数据,然后数据才被处理从而呈现到网页上,这其实就是
     8 发送了一个ajax请求。
     9 Web发展的趋势来看,这种形式的页面越来越多,网页的原始HTML文档不会包含任何数据,
    10 数据都是通过统一加载后再呈现出来的,这样在We开发上可以做到前后端分离,而且降低服
    11 务器直接渲染页面带来的压力。
    12 所以如果遇到这样的页面,直接requests等库来抓取原始页面,是无法获取到有效数据的,
    13 这时需要分析网页后台接口发送的jax请求,如果可以用requests来模拟Ajax请求,那么就可以
    14 成功抓取了。
    15 Ajax,是利用JavaScript在保证页面不被刷新、页面链接不改变的情况下与服务器交换数据并更新
    16 部分网页的技术。
    17 Ajax作用:
    18 1.发送请求。     比如:GET、POST、DELETE等请求
    19 2.解析内容。     比如:服务器返回的XML、HTML、JSON等文本
    20 3.渲染网页。     比如:使用JavaScript在局部更新、删除页面数据
    21 在这里我们需要使用浏览器的开发者工具分析什么是ajax请求:
    22 打开Chrome,在百度搜索微博并登陆也可以选择不登陆。按F12或者鼠标右键检查(N),
    23 打开开发者工具,选择Network再选择XHR,然后滑动页面到底部,会发现出来很多Type是xhr类型的请求,
    24 点开其中一个进行查看,请求类型是GET,再点击Preview显示的是JSON字符串,里面包含数据,
    25 点开Response这是返回给我们最原始的数据,需要执行相应的渲染方法才会显示到浏览器的页面。
    26 
    27 获取请求连接:http://api1.t.qq.com/asyn/home.php?&time=1540886592&page=4&id=486297042606010&isrecom=0&apiType=14&apiHost=http%3A%2F%2Fapi.t.qq.com&_r=154937
    28 ?问号之后是请求参数,time是时间参数,page是页数,id可变随机数字,后面的参数都是固定的。
    29 点击Preview,分别点开info和talk这两个元素,里面包含了用户信息和请求内容,一共15条,
    30 这样请求一个ajax接口分别对应一页和15条数据

    1.实战1

    """微博首页数据抓取实战,根据ajax请求抓取微博首页数据到mongodb数据库"""
    import time
    import requests
    from urllib.parse import urlencode
    from pyquery import PyQuery
    from pymongo import MongoClient
    # 定义请求的URL前半部分
    base_url = "https://d.weibo.com/p/aj/discover/loading?"
    # 连接MongoDB数据库
    client = MongoClient()
    # 选择一个指定的数据库
    db = client["weibo"]
    # MongoDB每个数据库又包含许多集合collection,它类似于关系型数据库中的表.指定一个集合
    collection = db["weibo"]
    # 获取请求头信息
    headers = {
        "Host": "d.weibo.com",
        "Referer": "https://d.weibo.com/623751_4",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.81 Safari/537.36",
        "X-Requested-With": "XMLHttpRequest",
        "Cookie": "SINAGLOBAL=6864515739096.891.1548998185053; login_sid_t=2559f2c7dd7bb6887ac8cdf20d7638a4; cross_origin_proto=SSL; _s_tentry=www.baidu.com; Apache=4081118444726.1284.1549420746515; ULV=1549420746520:4:4:3:4081118444726.1284.1549420746515:1549377935756; appkey=; crossidccode=CODE-gz-1GRdhO-gmK0X-y3uIsLpXUizmHrk0b096d; ALF=1580957408; SSOLoginState=1549421409; SUB=_2A25xXjsxDeThGeBH41QV9SbPyzqIHXVSKiv5rDV8PUNbmtBeLRjnkW9NQbEvXVwxASJ0zK19HvDa8bI8wpwLEiKj; SUBP=0033WrSXqPxfM725Ws9jqgMF55529P9D9W5_J5gZ9_ArKvxLE7cIcqAI5JpX5KzhUgL.Foq41hqXSKn0ehq2dJLoIpQ_BEH81F-RBC-RebXLxK-LBK-L12qN1KqES5tt; SUHB=0JvwbnCYpHgJl1; UOR=,,login.sina.com.cn; wvr=6; YF-Page-G0=0f25bf37128de43a8f69dd8388468211; wb_view_log_6986458306=1536*8641.25"
    }
    
    def get_page(page, time):
        # 构造参数字典并发起请求
        params = {
            "ajwvr": "6",
            "id": "623751_4",
            "uid": "6986458306",
            "page": page,
            "__rnd": time
        }
        # 调用urlencode()方法把参数转换为URL的GET请求参数
        url = base_url + urlencode(params)
        try:
            # 传递url和请求头信息请求网页内容
            response = requests.get(url, headers=headers)
            # 判断响应的状态码
            if response.status_code == 200:
                # 返回请求的json格式数据
                return response.json()
        except requests.ConnectionError as e:
            # 出现异常则打印
            print("Error", e.args)
    
    def parse_page(json):
        # 解析ajax返回的json字符串
        if json:
            html = json.get("data").get("html")
            # 传递html内容初始化
            pq = PyQuery(html)
            # 使用css选择器并调用items()方法生成可迭代数据
            items = pq(".text_box").items()
            for item in items:
                # 空字典要放进循环里面来,因为数据的解析是在循环里面完成,一次循环一次解析
                weibo = {}
                # 调用find()方法传入css节点选择然后调用text()进行文本处理
                weibo["title"] = item.find("div a").text()
                weibo["text"] = item.find("div").text()
                # yield生成器保存数据为一个对象,节省内存空间
                yield weibo
    
    def save_to_mongo(result):
        # 存储到MongoDB数据库
        if collection.insert(result):
            print("Saved to Mongo")
    
    if __name__ == "__main__":
        # 获取时间参数
        time = int(time.time() * 1000)
        # 设置需要的页数
        for page in range(1, 11):
            # 调用函数传递页数和时间参数
            json = get_page(page, time)
            # 调用解析函数传递返回的json文本完成解析
            results = parse_page(json)
            # 循环获取生成器中的数据
            for result in results:
                print(result)
                # 调用存储到mongodb的函数完成数据入库
                save_to_mongo(result)
    View Code

    2.实战2

    """今日头条街拍美图实战,根据ajax请求分类别多进程下载街拍美图到本地文件"""
    import os
    import requests
    from urllib.parse import urlencode
    from hashlib import md5
    from multiprocessing.pool import Pool
    
    
    def get_page(offset):
        # 设置url参数和获取该url请求结果
        params = {
            "aid": "24",
            "offset": offset,
            "format": "json",
            "keyword": "街拍",
            "autoload": "true",
            "count": "20",
            "cur_tab": "1",
            "from": "search_tab",
            "pd": "synthesis",
        }
        # 调用urlencode()方法拼接url
        url = "https://www.toutiao.com/api/search/content/?" + urlencode(params)
        try:
            # 发起请求并获取结果
            res = requests.get(url)
            # 请求成功返回字符串的json格式
            if res.status_code == 200:
                return res.json()
        except requests.ConnectionError as e:
            # 请求失败,打印结果
            print("连接错误", e.args)
    
    def get_images(json):
        # 如果有获取到数据就解析
        if json.get("data"):
            for item in json.get("data"):
                # 遍历获取需要的信息
                title = item.get("title")
                images = item.get("image_list")
                for image in images:
                    # 遍历已获取的多个图片
                    yield {
                        # yield生成器保存数据为一个对象,节省内存空间
                        "image": image.get("url"),
                        "title": title
                    }
    
    def save_image(item):
        # 去重并保存图片
        if not os.path.exists(item.get("title")):
            # 调用os模块的exists()方法判断当前文件夹下是否没有此名称的文件
            # 调用mkdir()方法创建此文件
            os.mkdir(item.get("title"))
        try:
            # 开始请求图片的url连接
            res = requests.get(item.get("image"))
            if res.status_code == 200:
                # 请求成功之后拼接图片下载地址。图片名称为了避免重复则使用md5加密
                file_path = "{0}/{1}.{2}".format(item.get("title"), md5(res.content).hexdigest(), "jpg")
                if not os.path.exists(file_path):
                    # 去除重复下载的图片并写入新图片。'wb'二进制形式写入
                    with open(file_path, "wb") as f:
                        f.write(res.content)
                else:
                    # 重复下载
                    print("Already Downloaded", file_path)
        except requests.ConnectionError as e:
            print("图片获取失败")
    
    def main(offset):
        # 调用各功能函数并传入参数即各进程任务
        json = get_page(offset)
        for item in get_images(json):
            print(item)
            save_image(item)
    
    if __name__ == "__main__":
        pool = Pool()
        page_list = ([x * 20 for x in range(1, 21)])
        # 调用进程池中的map()方法传入main函数和可迭代的列表
        pool.map(main, page_list)
        # 关闭进程池pool,使其不在接受新的(主进程)任务
        pool.close()
        # 主进程阻塞后,让子进程继续运行完成,子进程运行完后,再把主进程全部关掉。
        pool.join()
    View Code
  • 相关阅读:
    hdu 3666 差分约束系统
    hdu 1198农田灌溉
    常微分方程(阿諾爾德) Page 45 相空間,相流,運動,相曲線 註記
    高等微積分(高木貞治) 1.4節 例2
    常微分方程(阿諾爾德) Page 45 相空間,相流,運動,相曲線 註記
    解析函數論 Page 29 命題(2) 函數模的有界性
    高等微積分(高木貞治) 1.4節 例2
    解析函數論 Page 29 命題(1) 有界閉集上的一致連續性
    解析函數論 Page 29 命題(3) 模的下界的可達性
    解析函數論 Page 29 命題(2) 函數模的有界性
  • 原文地址:https://www.cnblogs.com/Guishuzhe/p/10356784.html
Copyright © 2011-2022 走看看