本问包含内容,Ajax数据获取,线程池简单使用,xlwt模板数据写入exel
有时候我们在用 requests 抓取页面的时候,得到的结果可能和在浏览器中看到的不一样:在浏览器中可以看到正常显示的页面数据,但是使用 requests 得到的结果并没有。这是因为 requests 获取的都是原始的 HTML 文档,而浏览器中的页面则是经过 JavaScript 处理数据后生成的结果,这些数据的来源有多种,可能是通过 Ajax 加载的,可能是包含在 HTML 文档中的,也可能是经过 JavaScript 和特定算法计算后生成的。
目录
1.导入模块
2.获取请求头
3.获取Ajax加载数据
4.解析页面信息
5.将获取到的数据写入exel
6.开辟线程池运行程序
7.相对完整代码
下面我们以新发地为例获取Ajax动态加载的内容
1.导入模块
注意看代码注释
import requestsimport xlwt # 用于将数据储存到exel文件from concurrent.futures import ThreadPoolExecutor # 导入线程池给爬虫加速
简单请求我们使用requests就好
2.获取请求头
在开发者面板找到如下请求头,这是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数据需要加入的请求}
链接可以根据实际情况填入
3.获取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提取数据
4.解析页面信息
将获取到的信息都保存到字典当中
如果想要运行注意根据实际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"))
5.将获取到的数据写入exel
这里细节比较多,不熟悉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文件
6.开辟线程池运行程序
简单的线程池使用
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("数据储存完成")
7.相对完整代码
为什么是相对完整呢?里面User-Agent还需要自行填入
import requestsimport 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("数据储存完成")
下期见