文章目录
声明
前面有了 selenium的基础,这里就拿虎牙直播页面来做一个实战测试,这是作为学习,测试使用,并不用作为商业用途,不刻意损害他人利益
实现流程给你
主播列表页面分析
我们需要把中间部分的列表页面内容给爬取下来,包括直播间封面,名称,主播昵称,头像,热度,游戏类别等。
从他的页面结构可以看出,其 xpath就是:
xml
//div[@class="box-bd"]//ul[@id="js-live-list"]/li
登录遮罩层处理
从页面结构来看,登录弹窗是在一个 id="UDBSdkLgn_iframe"的iframe中,所以我们在这里在进来时候,需要先切换到 iframe中,然后将窗口关闭
- 代码实现:
- 开启隐式等待,等待元素加载完成
- 将句柄切换到 iframe中,关闭登录弹窗
- 从 iframe中切除,回到主页面
python
...
# 隐式等待 最大10秒
self.driver.implicitly_wait(10)
# 关闭登录弹窗,需要切换到弹窗的iframe中
self.driver.switch_to.frame('UDBSdkLgn_iframe')
self.driver.find_element(by=By.ID, value="close-udbLogin").click()
# 从弹窗的iframe中切换回主页面
self.driver.switch_to.default_content()
...
解析直播列表的数据
根据 xpath 分析出各个元素的内容,在这里封装了一个函数,在每一页去拉取时调用它,在这里为了避免数据取不到的情况报错,我就加了 try...catch 代码如下:
python
def parse(self, page):
"""
解析虎牙直播的数据
@param data_list:属于引用数据
@return:
"""
list_data = []
room_list = self.driver.find_elements(by=By.XPATH, value='//div[@class="box-bd"]//ul[@id="js-live-list"]/li')
for key, elem in enumerate(room_list):
# 获取每个直播间的 链接,封面,直播间名字,主播名称,热度,游戏类名组成字典
tmp_dic = {}
# 直播间链接
try:
tmp_dic['link'] = elem.find_element(by=By.XPATH, value='./a[1]').get_attribute('href')
except:
tmp_dic['link'] = ''
pass
# 直播间封面
try:
tmp_dic['cover'] = elem.find_element(by=By.XPATH, value='./a[1]/img').get_attribute('src')
except:
tmp_dic['cover'] = ''
pass
# 直播间名字
try:
tmp_dic['name'] = elem.find_element(by=By.XPATH, value='./a[2]').text
except:
tmp_dic['name'] = ''
pass
# 主播头像
try:
tmp_dic['user-cover'] = elem.find_element(by=By.XPATH,
value='.//span[contains(@class,"avatar")]/img').get_attribute(
'src')
except:
tmp_dic['user-cover'] = ''
pass
# 主播昵称
try:
tmp_dic['user-name'] = elem.find_element(by=By.XPATH, value='.//span[contains(@class,"avatar")]/i').text
except:
tmp_dic['user-name'] = ''
pass
# 游戏名称
try:
tmp_dic['game-name'] = elem.find_element(by=By.XPATH,
value='.//span[contains(@class,"game-type")]/a').text
except:
tmp_dic['game-name'] = ''
pass
# 游戏链接
try:
tmp_dic['game-link'] = elem.find_element(by=By.XPATH,
value='.//span[contains(@class,"game-type")]/a').get_attribute(
'href')
except:
tmp_dic['game-link'] = ''
pass
# 热度
try:
tmp_dic['hot'] = elem.find_element(by=By.XPATH, value='.//span[@class="num"]/i[@class="js-num"]').text
except:
tmp_dic['hot'] = ''
pass
# 新增时间
tmp_dic['insert_time'] = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))
# 将数据写入列表
list_data.append(tmp_dic)
pass
# 将数据写入文件
with open(f'huya{page}.json', 'a', encoding="utf-8") as f:
json.dump(list_data, f, indent=4, sort_keys=False)
pass
return list_data
分页处理
因为分页结构在最下面,所以需要来用 js做一个页面滚动,滚动时,水平方向不用变,垂直放下往下滚动到最下面就行
- 代码片段:
python
# 滚动到底部
self.driver.execute_script("window.scrollTo(0, 10000);")
self.driver.find_element(by=By.XPATH,value='//div[@id="js-list-page"]//a[@class="laypage_next"]').click()
完整的代码
我在这里是爬取前10页,所以在在解析和分页的外面套了一层循环,再每一页分析完成,入库到 mongodb中,具体看如下完整代码:
python
import json
import time
from selenium import webdriver
from selenium.webdriver.chrome.service import Service as ChromService
from selenium.webdriver.common.by import By
from db.mongo_pool import MongoPool
class GetHuyaDatas(object):
def __init__(self):
self.url = 'https://www.huya.com/l'
# 显示设置驱动的路径,这是 selenium4之后的新写法,主要是为了解决 selenium打开浏览器慢的问题
service = ChromService(executable_path="/usr/local/bin/chromedriver")
self.driver = webdriver.Chrome(service=service)
pass
def run(self):
self.driver.get(self.url)
# 关闭登录弹窗
try:
# 隐式等待 最大10秒
self.driver.implicitly_wait(10)
# 关闭登录弹窗,需要切换到弹窗的iframe中
self.driver.switch_to.frame('UDBSdkLgn_iframe')
self.driver.find_element(by=By.ID, value="close-udbLogin").click()
# 从弹窗的iframe中切换回主页面
self.driver.switch_to.default_content()
except Exception as e:
print(e)
pass
# 隐式等待 最大15秒,等待ajax请求完成
self.driver.implicitly_wait(15)
# 遍历10页
for i in range(1, 11):
print(f'正在抓取第{i}页')
time.sleep(1)
with open(f'huya{i}.html', 'w') as f:
f.write(self.driver.page_source)
# 解析
data_list = self.parse(i)
self.save_data(data_list)
# 点击下一页,获取新的页面内容
try:
# 滚动到底部
self.driver.execute_script("window.scrollTo(0, 100000);")
self.driver.find_element(by=By.XPATH,
value='//div[@id="js-list-page"]//a[@class="laypage_next"]').click()
except:
print('已经到最后一页了')
pass
time.sleep(5)
self.driver.quit()
pass
def save_data(self, data_list):
"""
保存数据到mongodb
@param data_list:
@return:
"""
MongoPool().test.huya.insert_many(data_list)
def parse(self, page):
"""
解析虎牙直播的数据
@param data_list:属于引用数据
@return:
"""
list_data = []
room_list = self.driver.find_elements(by=By.XPATH, value='//div[@class="box-bd"]//ul[@id="js-live-list"]/li')
for key, elem in enumerate(room_list):
# 获取每个直播间的 链接,封面,直播间名字,主播名称,热度,游戏类名组成字典
tmp_dic = {}
# 直播间链接
try:
tmp_dic['link'] = elem.find_element(by=By.XPATH, value='./a[1]').get_attribute('href')
except:
tmp_dic['link'] = ''
pass
# 直播间封面
try:
tmp_dic['cover'] = elem.find_element(by=By.XPATH, value='./a[1]/img').get_attribute('src')
except:
tmp_dic['cover'] = ''
pass
# 直播间名字
try:
tmp_dic['name'] = elem.find_element(by=By.XPATH, value='./a[2]').text
except:
tmp_dic['name'] = ''
pass
# 主播头像
try:
tmp_dic['user-cover'] = elem.find_element(by=By.XPATH,
value='.//span[contains(@class,"avatar")]/img').get_attribute(
'src')
except:
tmp_dic['user-cover'] = ''
pass
# 主播昵称
try:
tmp_dic['user-name'] = elem.find_element(by=By.XPATH, value='.//span[contains(@class,"avatar")]/i').text
except:
tmp_dic['user-name'] = ''
pass
# 游戏名称
try:
tmp_dic['game-name'] = elem.find_element(by=By.XPATH,
value='.//span[contains(@class,"game-type")]/a').text
except:
tmp_dic['game-name'] = ''
pass
# 游戏链接
try:
tmp_dic['game-link'] = elem.find_element(by=By.XPATH,
value='.//span[contains(@class,"game-type")]/a').get_attribute(
'href')
except:
tmp_dic['game-link'] = ''
pass
# 热度
try:
tmp_dic['hot'] = elem.find_element(by=By.XPATH, value='.//span[@class="num"]/i[@class="js-num"]').text
except:
tmp_dic['hot'] = ''
pass
# 新增时间
tmp_dic['insert_time'] = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))
# 将数据写入列表
list_data.append(tmp_dic)
pass
# 将数据写入文件
with open(f'huya{page}.json', 'a', encoding="utf-8") as f:
json.dump(list_data, f, indent=4, sort_keys=False)
pass
return list_data
pass
if __name__ == '__main__':
huya = GetHuyaDatas()
huya.run()
selenium自定点击分页:
打印分页的效果