• 动态加载内容爬取,Ajax爬取典例


    本问包含内容,Ajax数据获取,线程池简单使用,xlwt模板数据写入exel

    有时候我们在用 requests 抓取页面的时候,得到的结果可能和在浏览器中看到的不一样:在浏览器中可以看到正常显示的页面数据,但是使用 requests 得到的结果并没有。这是因为 requests 获取的都是原始的 HTML 文档,而浏览器中的页面则是经过 JavaScript 处理数据后生成的结果,这些数据的来源有多种,可能是通过 Ajax 加载的,可能是包含在 HTML 文档中的,也可能是经过 JavaScript 和特定算法计算后生成的。

    目录

    1.导入模块

    2.获取请求头

    3.获取Ajax加载数据

    4.解析页面信息

    5.将获取到的数据写入exel

    6.开辟线程池运行程序

    7.相对完整代码


    下面我们以新发地为例获取Ajax动态加载的内容

    1.导入模块

    注意看代码注释

    1. import requests
    2. import xlwt # 用于将数据储存到exel文件
    3. from concurrent.futures import ThreadPoolExecutor # 导入线程池给爬虫加速

    简单请求我们使用requests就好 

    2.获取请求头

    在开发者面板找到如下请求头,这是Ajax数据获取所需的请求头

    如果想要运行注意根据实际情况修改请求头参数

    1. base_url = "http://xinfadi.com.cn/getPriceData.html"
    2. headers = {
    3. 'Host': 'xinfadi.com.cn', # 在开发者面板都可以找到的请求头
    4. 'Origin': 'http://xinfadi.com.cn',
    5. 'Referer': 'http://xinfadi.com.cn/priceDetail.html',
    6. 'User-Agent': '', # 填写自己的User-Agent
    7. 'X-Requested-With': 'XMLHttpRequest' # 获取Ajax数据需要加入的请求
    8. }

    链接可以根据实际情况填入

    3.获取Ajax加载数据

    1. # 爬取页面原码(Ajax)
    2. def get_page(url, headers, current):
    3. print(f"正在爬取第{current}页")
    4. # post请求需要的参数在Payload可找到
    5. params = {
    6. 'limit': 20, # 页面最多显示数据的条数,10或20
    7. 'current': current, # 页数,第几页
    8. 'pubDateStartTime': '',
    9. 'pubDateEndTime': '',
    10. 'prodPcatid': '',
    11. 'prodCatid': '',
    12. 'prodName': ''
    13. }
    14. response = requests.post(url, data=params, headers=headers) # 根据网页提示使用post请求
    15. json_source = response.json()["list"] # 将Ajax数据转化为json格式后提取list条目的内容
    16. page_parse(json_source, all_info) # 调用page_parse提取数据

    4.解析页面信息

    将获取到的信息都保存到字典当中

    如果想要运行注意根据实际get_page函数返回的json数据给字典的键(key)赋值

    如一级分类是否对应prodCat

    1. # 提取数据
    2. def page_parse(json_source, all_info): # all_info为列表,包含每件产品的信息
    3. for item in json_source: # type(item)为字典
    4. all_info["一级分类"].append(item.get("prodCat")) # value对应的每个列表包含有所有产品的数据
    5. all_info["二级分类"].append(item.get("prodPcat"))
    6. all_info["产品名"].append(item.get("prodName"))
    7. all_info["最高价"].append(item.get("highPrice"))
    8. all_info["最低价"].append(item.get("lowPrice"))
    9. all_info["平均价"].append(item.get("avgPrice"))
    10. all_info["规格"].append(item.get("specInfo"))
    11. all_info["产地"].append(item.get("place"))
    12. all_info["单位"].append(item.get("unitInfo"))
    13. all_info["发布日期"].append(item.get("pubDate"))

    5.将获取到的数据写入exel

    这里细节比较多,不熟悉xlwt模块的可以跳过

    1. # 保存数据
    2. def save_data_exel(all_info):
    3. book = xlwt.Workbook(encoding="utf-8") # 打开新工作薄
    4. sheet = book.add_sheet('新发地', cell_overwrite_ok=True) # cell_overwrite_ok=Tru时重复写入单元格不会报错
    5. col = list(all_info.keys()) # col获取all_info的键将作为标题头写入exel
    6. value = list(all_info.values()) # value则是作为产品内容写入exel
    7. for i in range(10): # 标题头写入操作
    8. sheet.write(0, i, col[i])
    9. # 根据all_info的格式特点采取按列储存数据
    10. for j in range(10): # 列
    11. for k in range(20*(MAX_PAGE-1)): # 行。MAX_PAGE无法取0,因为current从1开始,故MAX_PAGE-1
    12. sheet.write(k+1, j, value[j][k]) # 为了不覆盖第一行的标题行从第1行写入,而不是第0行
    13. book.save('新发地商品信息.xls') # 文件名+保存Exel文件

    6.开辟线程池运行程序

    简单的线程池使用

    1. if __name__ == '__main__':
    2. base_url = "http://xinfadi.com.cn/getPriceData.html"
    3. headers = {
    4. 'Host': 'xinfadi.com.cn', # 在开发者面板都可以找到的请求头
    5. 'Origin': 'http://xinfadi.com.cn',
    6. 'Referer': 'http://xinfadi.com.cn/priceDetail.html',
    7. 'User-Agent': '', # 填写自己的User-Agent
    8. 'X-Requested-With': 'XMLHttpRequest' # 获取Ajax数据需要加入的请求
    9. }
    10. all_info = { # all_info包含获取到的所有产品数据
    11. "一级分类": [],
    12. "二级分类": [],
    13. "产品名": [],
    14. "最高价": [],
    15. "最低价": [],
    16. "平均价": [],
    17. "规格": [],
    18. "产地": [],
    19. "单位": [],
    20. "发布日期": []
    21. }
    22. with ThreadPoolExecutor(50) as T: # 创建包含50个线程的线程池
    23. for i in range(1, MAX_PAGE): # post的current参数从1开始不能取0
    24. # 不能写成T.submit(get_page, base_url=base_url, headers=headers, current=i)不然获取不到数据
    25. T.submit(get_page, base_url, headers, i) # 将函数调用的任务交给线程池来做
    26. save_data_exel(all_info)
    27. print("数据储存完成")

    7.相对完整代码

    为什么是相对完整呢?里面User-Agent还需要自行填入

    1. import requests
    2. import xlwt # 用于将数据储存到exel文件
    3. from concurrent.futures import ThreadPoolExecutor # 导入线程池给爬虫加速
    4. MAX_PAGE = 200 # 爬取最大页数>=1,因为range(1,1)为None
    5. # 爬取页面原码(Ajax)
    6. def get_page(url, headers, current):
    7. print(f"正在爬取第{current}页")
    8. # post请求需要的参数在Payload可找到
    9. params = {
    10. 'limit': 20, # 页面最多显示数据的条数,10或20
    11. 'current': current, # 页数,第几页
    12. 'pubDateStartTime': '',
    13. 'pubDateEndTime': '',
    14. 'prodPcatid': '',
    15. 'prodCatid': '',
    16. 'prodName': ''
    17. }
    18. response = requests.post(url, data=params, headers=headers) # 根据网页提示使用post请求
    19. json_source = response.json()["list"] # 将Ajax数据转化为json格式后提取list条目的内容
    20. page_parse(json_source, all_info) # 调用page_parse提取数据
    21. # 提取数据
    22. def page_parse(json_source, all_info): # all_info为列表,包含每件产品的信息
    23. for item in json_source: # type(item)为字典
    24. all_info["一级分类"].append(item.get("prodCat")) # value对应的每个列表包含有所有产品的数据
    25. all_info["二级分类"].append(item.get("prodPcat"))
    26. all_info["产品名"].append(item.get("prodName"))
    27. all_info["最高价"].append(item.get("highPrice"))
    28. all_info["最低价"].append(item.get("lowPrice"))
    29. all_info["平均价"].append(item.get("avgPrice"))
    30. all_info["规格"].append(item.get("specInfo"))
    31. all_info["产地"].append(item.get("place"))
    32. all_info["单位"].append(item.get("unitInfo"))
    33. all_info["发布日期"].append(item.get("pubDate"))
    34. # 保存数据
    35. def save_data_exel(all_info):
    36. book = xlwt.Workbook(encoding="utf-8") # 打开新工作薄
    37. sheet = book.add_sheet('新发地', cell_overwrite_ok=True) # cell_overwrite_ok=Tru时重复写入单元格不会报错
    38. col = list(all_info.keys()) # col获取all_info的键将作为标题头写入exel
    39. value = list(all_info.values()) # value则是作为产品内容写入exel
    40. for i in range(10): # 标题头写入操作
    41. sheet.write(0, i, col[i])
    42. # 根据all_info的格式特点采取按列储存数据
    43. for j in range(10): # 列
    44. for k in range(20*(MAX_PAGE-1)): # 行。MAX_PAGE无法取0,因为current从1开始,故MAX_PAGE-1
    45. sheet.write(k+1, j, value[j][k]) # 为了不覆盖第一行的标题行从第1行写入,而不是第0行
    46. book.save('新发地商品信息.xls') # 文件名+保存Exel文件
    47. if __name__ == '__main__':
    48. base_url = "http://xinfadi.com.cn/getPriceData.html"
    49. headers = {
    50. 'Host': 'xinfadi.com.cn', # 在开发者面板都可以找到的请求头
    51. 'Origin': 'http://xinfadi.com.cn',
    52. 'Referer': 'http://xinfadi.com.cn/priceDetail.html',
    53. 'User-Agent': '', # 填写自己的User-Agent
    54. 'X-Requested-With': 'XMLHttpRequest' # 获取Ajax数据需要加入的请求
    55. }
    56. all_info = { # all_info包含获取到的所有产品数据
    57. "一级分类": [], # value对应的每个列表包含有所有产品的数据
    58. "二级分类": [],
    59. "产品名": [],
    60. "最高价": [],
    61. "最低价": [],
    62. "平均价": [],
    63. "规格": [],
    64. "产地": [],
    65. "单位": [],
    66. "发布日期": []
    67. }
    68. with ThreadPoolExecutor(50) as T: # 创建包含50个线程的线程池
    69. for i in range(1, MAX_PAGE): # post的current参数从1开始不能取0
    70. # 不能写成T.submit(get_page, base_url=base_url, headers=headers, current=i)不然获取不到数据
    71. T.submit(get_page, base_url, headers, i) # 将函数调用的任务交给线程池来做
    72. save_data_exel(all_info)
    73. print("数据储存完成")

    下期见

  • 相关阅读:
    2022年千元半入耳式蓝牙耳机,高性价比蓝牙耳机推荐
    开放式激光振镜运动控制器(五):ZMC408SCAN光纤激光器的能量控制
    创建一个 Github 统计项目
    代码随想录-刷题第二天
    查找已注册的 Spring Security 过滤器
    28.CSS 渐变圆文本动画
    【无标题】清空吗
    在 MATLAB 中,如何高效地处理大规模矩阵运算以提高程序的运行速度?
    排序算法一 直接插入排序,希尔排序,直接选择排序,堆排序和冒泡排序
    CDH Kerberos启动后hue报错Couldn‘t renew kerberos ticket
  • 原文地址:https://blog.csdn.net/m0_61791601/article/details/125889849