本问包含内容,Ajax数据获取,线程池简单使用,xlwt模板数据写入exel
有时候我们在用 requests 抓取页面的时候,得到的结果可能和在浏览器中看到的不一样:在浏览器中可以看到正常显示的页面数据,但是使用 requests 得到的结果并没有。这是因为 requests 获取的都是原始的 HTML 文档,而浏览器中的页面则是经过 JavaScript 处理数据后生成的结果,这些数据的来源有多种,可能是通过 Ajax 加载的,可能是包含在 HTML 文档中的,也可能是经过 JavaScript 和特定算法计算后生成的。
目录
下面我们以新发地为例获取Ajax动态加载的内容
注意看代码注释
- import requests
- import xlwt # 用于将数据储存到exel文件
- from concurrent.futures import ThreadPoolExecutor # 导入线程池给爬虫加速
简单请求我们使用requests就好
在开发者面板找到如下请求头,这是Ajax数据获取所需的请求头
如果想要运行注意根据实际情况修改请求头参数
- base_url = "http://xinfadi.com.cn/getPriceData.html"
- headers = {
- 'Host': 'xinfadi.com.cn', # 在开发者面板都可以找到的请求头
- 'Origin': 'http://xinfadi.com.cn',
- 'Referer': 'http://xinfadi.com.cn/priceDetail.html',
- 'User-Agent': '', # 填写自己的User-Agent
- 'X-Requested-With': 'XMLHttpRequest' # 获取Ajax数据需要加入的请求
- }
链接可以根据实际情况填入
- # 爬取页面原码(Ajax)
- def get_page(url, headers, current):
- print(f"正在爬取第{current}页")
- # post请求需要的参数在Payload可找到
- params = {
- 'limit': 20, # 页面最多显示数据的条数,10或20
- 'current': current, # 页数,第几页
- 'pubDateStartTime': '',
- 'pubDateEndTime': '',
- 'prodPcatid': '',
- 'prodCatid': '',
- 'prodName': ''
- }
- response = requests.post(url, data=params, headers=headers) # 根据网页提示使用post请求
- json_source = response.json()["list"] # 将Ajax数据转化为json格式后提取list条目的内容
- page_parse(json_source, all_info) # 调用page_parse提取数据
将获取到的信息都保存到字典当中
如果想要运行注意根据实际get_page函数返回的json数据给字典的键(key)赋值
如一级分类是否对应prodCat
- # 提取数据
- def page_parse(json_source, all_info): # all_info为列表,包含每件产品的信息
- for item in json_source: # type(item)为字典
- all_info["一级分类"].append(item.get("prodCat")) # value对应的每个列表包含有所有产品的数据
- all_info["二级分类"].append(item.get("prodPcat"))
- all_info["产品名"].append(item.get("prodName"))
- all_info["最高价"].append(item.get("highPrice"))
- all_info["最低价"].append(item.get("lowPrice"))
- all_info["平均价"].append(item.get("avgPrice"))
- all_info["规格"].append(item.get("specInfo"))
- all_info["产地"].append(item.get("place"))
- all_info["单位"].append(item.get("unitInfo"))
- all_info["发布日期"].append(item.get("pubDate"))
这里细节比较多,不熟悉xlwt模块的可以跳过
- # 保存数据
- def save_data_exel(all_info):
- book = xlwt.Workbook(encoding="utf-8") # 打开新工作薄
- sheet = book.add_sheet('新发地', cell_overwrite_ok=True) # cell_overwrite_ok=Tru时重复写入单元格不会报错
- col = list(all_info.keys()) # col获取all_info的键将作为标题头写入exel
- value = list(all_info.values()) # value则是作为产品内容写入exel
- for i in range(10): # 标题头写入操作
- sheet.write(0, i, col[i])
- # 根据all_info的格式特点采取按列储存数据
- for j in range(10): # 列
- for k in range(20*(MAX_PAGE-1)): # 行。MAX_PAGE无法取0,因为current从1开始,故MAX_PAGE-1
- sheet.write(k+1, j, value[j][k]) # 为了不覆盖第一行的标题行从第1行写入,而不是第0行
-
- book.save('新发地商品信息.xls') # 文件名+保存Exel文件
简单的线程池使用
- if __name__ == '__main__':
- base_url = "http://xinfadi.com.cn/getPriceData.html"
- headers = {
- 'Host': 'xinfadi.com.cn', # 在开发者面板都可以找到的请求头
- 'Origin': 'http://xinfadi.com.cn',
- 'Referer': 'http://xinfadi.com.cn/priceDetail.html',
- 'User-Agent': '', # 填写自己的User-Agent
- 'X-Requested-With': 'XMLHttpRequest' # 获取Ajax数据需要加入的请求
- }
- all_info = { # all_info包含获取到的所有产品数据
- "一级分类": [],
- "二级分类": [],
- "产品名": [],
- "最高价": [],
- "最低价": [],
- "平均价": [],
- "规格": [],
- "产地": [],
- "单位": [],
- "发布日期": []
- }
-
- with ThreadPoolExecutor(50) as T: # 创建包含50个线程的线程池
- for i in range(1, MAX_PAGE): # post的current参数从1开始不能取0
- # 不能写成T.submit(get_page, base_url=base_url, headers=headers, current=i)不然获取不到数据
- T.submit(get_page, base_url, headers, i) # 将函数调用的任务交给线程池来做
- save_data_exel(all_info)
- print("数据储存完成")
为什么是相对完整呢?里面User-Agent还需要自行填入
- import requests
- import xlwt # 用于将数据储存到exel文件
- from concurrent.futures import ThreadPoolExecutor # 导入线程池给爬虫加速
-
- MAX_PAGE = 200 # 爬取最大页数>=1,因为range(1,1)为None
-
-
- # 爬取页面原码(Ajax)
- def get_page(url, headers, current):
- print(f"正在爬取第{current}页")
- # post请求需要的参数在Payload可找到
- params = {
- 'limit': 20, # 页面最多显示数据的条数,10或20
- 'current': current, # 页数,第几页
- 'pubDateStartTime': '',
- 'pubDateEndTime': '',
- 'prodPcatid': '',
- 'prodCatid': '',
- 'prodName': ''
- }
- response = requests.post(url, data=params, headers=headers) # 根据网页提示使用post请求
- json_source = response.json()["list"] # 将Ajax数据转化为json格式后提取list条目的内容
- page_parse(json_source, all_info) # 调用page_parse提取数据
-
-
- # 提取数据
- def page_parse(json_source, all_info): # all_info为列表,包含每件产品的信息
- for item in json_source: # type(item)为字典
- all_info["一级分类"].append(item.get("prodCat")) # value对应的每个列表包含有所有产品的数据
- all_info["二级分类"].append(item.get("prodPcat"))
- all_info["产品名"].append(item.get("prodName"))
- all_info["最高价"].append(item.get("highPrice"))
- all_info["最低价"].append(item.get("lowPrice"))
- all_info["平均价"].append(item.get("avgPrice"))
- all_info["规格"].append(item.get("specInfo"))
- all_info["产地"].append(item.get("place"))
- all_info["单位"].append(item.get("unitInfo"))
- all_info["发布日期"].append(item.get("pubDate"))
-
-
-
- # 保存数据
- def save_data_exel(all_info):
- book = xlwt.Workbook(encoding="utf-8") # 打开新工作薄
- sheet = book.add_sheet('新发地', cell_overwrite_ok=True) # cell_overwrite_ok=Tru时重复写入单元格不会报错
- col = list(all_info.keys()) # col获取all_info的键将作为标题头写入exel
- value = list(all_info.values()) # value则是作为产品内容写入exel
- for i in range(10): # 标题头写入操作
- sheet.write(0, i, col[i])
- # 根据all_info的格式特点采取按列储存数据
- for j in range(10): # 列
- for k in range(20*(MAX_PAGE-1)): # 行。MAX_PAGE无法取0,因为current从1开始,故MAX_PAGE-1
- sheet.write(k+1, j, value[j][k]) # 为了不覆盖第一行的标题行从第1行写入,而不是第0行
-
- book.save('新发地商品信息.xls') # 文件名+保存Exel文件
-
-
- if __name__ == '__main__':
- base_url = "http://xinfadi.com.cn/getPriceData.html"
- headers = {
- 'Host': 'xinfadi.com.cn', # 在开发者面板都可以找到的请求头
- 'Origin': 'http://xinfadi.com.cn',
- 'Referer': 'http://xinfadi.com.cn/priceDetail.html',
- 'User-Agent': '', # 填写自己的User-Agent
- 'X-Requested-With': 'XMLHttpRequest' # 获取Ajax数据需要加入的请求
- }
- all_info = { # all_info包含获取到的所有产品数据
- "一级分类": [], # value对应的每个列表包含有所有产品的数据
- "二级分类": [],
- "产品名": [],
- "最高价": [],
- "最低价": [],
- "平均价": [],
- "规格": [],
- "产地": [],
- "单位": [],
- "发布日期": []
- }
-
- with ThreadPoolExecutor(50) as T: # 创建包含50个线程的线程池
- for i in range(1, MAX_PAGE): # post的current参数从1开始不能取0
- # 不能写成T.submit(get_page, base_url=base_url, headers=headers, current=i)不然获取不到数据
- T.submit(get_page, base_url, headers, i) # 将函数调用的任务交给线程池来做
- save_data_exel(all_info)
- print("数据储存完成")
下期见
