写在前面
对于刚接触爬虫的萌新来说,selenium方式是最友好的,虽然效率不高,但是比人工快,还保护眼睛,因为一开始没有用爬虫的时候,是一个个去人工操作的。
任务来源:其实是我想从一个业务网站下载坐标到本地,建库,方便项目管理,数据质检。
Round 1
一开始没想用selenium,因为效率低。然后下载了相关的软件,把页面分析了断断续续一个月也没找到突破口,因为坐标可能是敏感数据,所以进行了加密。但是导出的时候是可以。
Round 2
后来还特意买了爬虫的书籍来看,有点小题大作,加上书籍内容广泛但不是丰富,其实网上是资源更多,更便捷。研究半天还是决定先用selenium试下,因为着急用数据库成果。
正题
准备工作:安装好谷歌浏览器chorme和对应版本的浏览器驱动chromedriver。
首先要导入需要用到的库
from selenium import webdriver
from selenium.webdriver.common.by import By
import time #非常关键,因为selenium操作网页会有加载的时间,必须要有等待,等加载完毕后再操作。
import pandas as pd #读取xls用到
import os #重命名文件用到
实现路径是:
1.需要把项目清单xls文件导入python,添加做成列表,以便在后面根据调用这个列表进行逐一爬取。(这里我是直接在网上搜索到然后复制修改成自己想要的,还没有完全理解透)
#定义全局变量
result = []
#读入xls文件项目名称的函数
def excel_one_line_to_list():
df = pd.read_excel("D:\demo\Test_20211116224417原始.xls", usecols=[1],names=None) # 读取项目名称列,不要列名
df_li = df.values.tolist()
for s_li in df_li:
result.append(s_li[0])
if __name__ == '__main__':
excel_one_line_to_list()
2.调用浏览器驱动打开网页,通过预置的登录账号和密码登录网站()
# 创建 WebDriver 对象,指明使用chrome浏览器驱动
wd = webdriver.Chrome(r'd:\webdrivers\chromedriver.exe')
# 设置最大等待时长为 3秒
wd.implicitly_wait(3)
# 调用WebDriver 对象的get方法 可以让浏览器打开指定网址,怕有杠精,此处省略了真正的网址
wd.get('http://XXXX/TdIndex.html')
elements = wd.find_elements_by_xpath("//*[@id='txtLogin']")
element = wd.find_element_by_id("txtLogin")
element.clear() # 清除输入框已有的字符串
element.send_keys('qnz') # 输入新字符串
element = wd.find_element_by_id("txtPass")
element.clear() # 清除输入框已有的字符串
element.send_keys('123456\n') # 输入新字符串
#这里加个等待时间,因为网页打开是需要时间,如果不加后续可能会执行出错。
time.sleep(2)
3.登录完成后,跳转到第二个网页,因为登录成功后,页面会刷新到另一个页面,网址是不一样的,此时若不执行跳转,就无法进行下一步操作,因为程序会一直在原来的网址操作,它并不知道已经刷新了新的网址。(知识点:窗口切换)
# 获得打开的第一个窗口句柄
window_1 = wd.current_window_handle
# 获得打开的所有的窗口句柄
windows = wd.window_handles
# 切换到最新的窗口
for current_window in windows:
if current_window != window_1:
wd.switch_to.window(current_window)
#print(wd.current_url)#打印当前操作的网址#此处我注释调,调试的时候保留,以便验证确定是否已经跳转页面成功。
4.核心代码部分。这部分牵涉到的第一个知识点就是切换框架,因为要操作的元素有在第一级框架,有在第二级框架的,所以需要进入框架、切出框架、回到主页等操作。
第二个知识点就是异常处理。try/except的使用。因为是批量爬取,就会出现有些缺失的情况出现,所以不是所有的操作都能成功,这时候用try/except来处理非常合适。操作结束要关闭窗口,回到第一个窗口,如此循环。
#从网站下载文件的函数--↓--(开始)
def download(XM_name):
#print(wd.current_url) # 打印当前操作的网址
#切换进入框架
wd.switch_to.frame('frmleft')
#点击项目查询箱
wd.find_element(By.ID, 'menu_tree_5_span').click()
wd.switch_to.frame('page_84953F0A-2845-1B2F-B6A6-8ED4854DD400')
wd.switch_to.frame('yb')
#清除上次输入的项目名称
wd.find_element(By.NAME, 'ywname').clear()
#输入新的项目名称
wd.find_element(By.NAME, 'ywname').send_keys(XM_name)
#点击查询
wd.find_element(By.CLASS_NAME,'icon_find').click()
#等待缓冲
time.sleep(2)
#点击项目查看
wd.find_element_by_xpath("//div/a[last()-1]").click()
#切换到弹出的窗口(开始)
time.sleep(1)
# 获得打开的第一个窗口句柄
window_1 = wd.current_window_handle
# 获得打开的所有的窗口句柄
windows = wd.window_handles
# 切换到最新的窗口
for current_window in windows:
if current_window != window_1:
wd.switch_to.window(current_window)
#切换到弹出的窗口(结束)
try:
jie_duan='验收'
# 点击验收坐标导入
wd.find_element_by_xpath('//*[@id="rc_tree"]/li/ul/li[3]/ul/li[3]/div/span[5]').click()
time.sleep(1)
# 切换进入框架#切入第二frame(计数从0开始,因为frame的Id和name一致在变化,不能用它们来定位)
wd.switch_to.frame(1)
# 点击下载(项目区范围线)
wd.find_element_by_xpath('//*[@id="maingrid|2|r1001|c104"]/div/div/a').click()
# 切换进入内部frame
wd.switch_to.frame('_DialogFrame_0')
# 勾选shp
wd.find_element_by_xpath('//*[@value=".txt"]').click()
# 返回上一级frame
wd.switch_to.parent_frame()
# 点击确定
wd.find_element_by_xpath('//*[@id="_ButtonOK_0"]').click()
time.sleep(2) # 等待保存动作完结,防止超前操作,没有这个等待会直接改变上一个文件。
get(XM_name,jie_duan)
print(XM_name, '---验收坐标下载成功---')
except:
print(XM_name,'---无验收坐标,验收坐标下载失败!---')
try:
jie_duan = '计划'
wd.switch_to.default_content()
# 点击计划坐标导入
wd.find_element_by_xpath('//*[@id="rc_tree"]/li/ul/li[1]/ul/li[3]/div/span[5]').click()
time.sleep(1)
# 切换进入框架#切入第二frame(计数从0开始,因为frame的Id和name一致在变化,不能用它们来定位)
wd.switch_to.frame(2)
# 点击下载(项目区范围线)
wd.find_element_by_xpath('//*[@id="maingrid|2|r1001|c104"]/div/div/a').click()
# 切换进入内部frame
wd.switch_to.frame('_DialogFrame_0')
# 勾选shp
wd.find_element_by_xpath('//*[@value=".txt"]').click()
# 返回上一级frame
wd.switch_to.parent_frame()
# 点击确定
wd.find_element_by_xpath('//*[@id="_ButtonOK_0"]').click()
time.sleep(2) # 等待保存动作完结,防止超前操作,没有这个等待会直接改变上一个文件。
get(XM_name,jie_duan)
print(XM_name,'---计划坐标下载成功---')
except:
print(XM_name, '---无坐标,下载失败---')
wd.close()#关闭窗口
wd.switch_to.window(windows[0])#切换到第一个窗口
#print(wd.current_url) # 打印当前操作的网址
return
#从网站下载文件的函数--↑--(结束)
5.上一步调用了一个保存文件的时候对文件进行重命名的函数,因为下载的文件默认已经有名称了,而且下载时并没有重命名的机会,所以需要对下载保存目录下的最新文件进行重命名即可,名称就是从导入的项目名称传递进去即可。(这段代码也是从网上找的,修改了一下就用了)
#对下载的文件进行重命名的函数
def get(filename,j_d):
path = r'C:\Users\seawq\Downloads'
list1 = os.listdir(path)
list2 = [x for x in list1 if '.txt' in x] #获取文件夹内的后缀为txt的文件列表
list2.sort(key=lambda x:os.path.getmtime(path+'\\'+x)) #文件列表按!修改!时间排序
time.sleep(1)
old = os.path.join(path,list2[-1])
new = os.path.join(path,filename+'_'+j_d+'.txt')
os.rename(old,new)
return new#可以不用返回值,因为后面没有调用了
6.最后就是循环调用的部分了。最后关闭窗口,搞定!
#调用上面的函数,循环遍历整个列表
i=1
for Nresult in result:
download(Nresult)
i+=1
print(str(i)+'/1709')
print('-------运行完毕-------')
wd.quit()
小结
其实这次用selenium来实现,花时间最多的就是frame(框架)的切换,研究了很久。关于selenium选择元素的方法,学习了css和Xpath,但是现在流行的是Xpath,也相对简单,主流浏览器的检查要素页面都自带提供右键复制Xpath路径和Xpath绝对路径的功能,非常好用,不用你去琢磨应该用ID还是什么来选择,直接复制粘贴到程序里就能用(当然一定要考虑是否有frame,复制的Xpath路径是不考虑frame的,因为我被坑过)。总之对于想学爬虫的初学者来说selenium是非常好的选择,虽然高手都说用selenium来实现不算爬虫,只能说是自动化(seleninum的初衷就是为了做自动化测试的)。