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=15493728 ?问号之后是请求参数,time是时间参数,page是页数,id可变随机数字,后面的参数都是固定的。29 点击Preview,分别点开info和talk这两个元素,里面包含了用户信息和请求内容,一共15条,30 这样请求一个ajax接口分别对应一页和15条数据
1.实战1
"""微博首页数据抓取实战,根据ajax请求抓取微博首页数据到mongodb数据库"""import timeimport requestsfrom urllib.parse import urlencodefrom pyquery import PyQueryfrom 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 weibodef 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)
2.实战2
"""今日头条街拍美图实战,根据ajax请求分类别多进程下载街拍美图到本地文件"""import osimport requestsfrom urllib.parse import urlencodefrom hashlib import md5from multiprocessing.pool import Pooldef 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()
Ajax异步数据抓取
原文地址:https://www.cnblogs.com/Guishuzhe/p/10356784.html