本问包含内容,Ajax数据获取,线程池简单使用,xlwt模板数据写入exel
有时候我们在用 requests 抓取页面的时候,得到的结果可能和在浏览器中看到的不一样:在浏览器中可以看到正常显示的页面数据,但是使用 requests 得到的结果并没有。这是因为 requests 获取的都是原始的 HTML 文档,而浏览器中的页面则是经过 JavaScript 处理数据后生成的结果,这些数据的来源有多种,可能是通过 Ajax 加载的,可能是包含在 HTML 文档中的,也可能是经过 JavaScript 和特定算法计算后生成的。
目录
下面我们以新发地为例获取Ajax动态加载的内容
1.导入模块
注意看代码注释
import requests
import 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 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("数据储存完成")
下期见