Selenium是一个自动化测试工具,利用它可以驱动浏览器执行特定的动作(具体的配置或使用可以百度)。我用的谷歌浏览器,先在镜像下载谷歌浏览器版本对应的驱动版本https://npm.taobao.org/,下下来直接解压就能用了。
代码只是个简单的练习,对于异常情况没有过多的处理,也没有代理、登录、验证什么的,只是能用(或许网络不好的话需要把超时和睡眠时间加长一点点)。另外,对于数据的解析我也写得很简单,导致商品名中的换行符也没去掉,以及商品有套装的情况也没处理(导致所有套装项的商品名和价格合并在了一起)。我的处理流程就是,根据搜索内容一页一页的解析,将解析结果存到文本里,判断翻页栏的总页数和当前页数进行翻页,递归直到最后一页。
还有个没解决的问题就是,因为搜索结果会动态加载,需要往下拉滚动条,我也不知道啥时候才算是全部加载完了,看别人博客写的是第一次滑到底是30个,等加载完再滑到底就有60个。目前我是循环几次往下拉滚动条,但是不能保证一定加载完了。
代码如下:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from pyquery import PyQuery
import time
import json
# 数据写入文本
def write_to_file(content,filepath):
with open(filepath,'a',encoding='utf-8') as f:
f.write(json.dumps(content,ensure_ascii=False)+'\n')
# 解析爬出来的商品列表信息
def parse_jd_html(html):
#搜索出来的商品列表包裹在ul.gl-warp(为什么是warp不是wrap)下的li.gl-item中
#作为测试,只取出书名和价格
doc=PyQuery(html)
items=doc('ul.gl-warp li.gl-item').items()
for item in items:
yield{
'name':item.find('.p-name a em').text().strip(),
'price':item.find('.p-price strong i').text()
}
#根据keys进行爬取相关信息
def search_by_keys(keys):
#edge浏览器不用去下载驱动也能使用,我的版本为EdgeHTML 18
#browser=webdriver.Edge()
#谷歌浏览器需要下载驱动,可以去镜像下载对应版本https://npm.taobao.org/
#解压出来exe随便放哪里都能用
#chrome_options=webdriver.ChromeOptions()
#chrome_options.add_argument('--headless') #无界面模式
#browser=webdriver.Chrome(r'C:\Users\1992\AppData\Local\Google\Chrome\Application\chromedriver.exe',chrome_options=chrome_options)
browser=webdriver.Chrome(r'C:\Users\1992\AppData\Local\Google\Chrome\Application\chromedriver.exe')
wait=WebDriverWait(browser,20) #设置几秒超时时间
try:
browser.get('https://www.jd.com/') #京东首页
#print(browser.current_url)
#print(browser.get_cookies())
#print(browser.page_source)
# until当某条件成立则继续执行,注意后面方法参数传元祖
# 等待搜索框加载完
input=wait.until(EC.presence_of_element_located((By.ID,'key')))
# 等待搜索按钮可点击
#enter=wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,'.form button')))
input.clear()
input.send_keys(keys)
input.send_keys(Keys.ENTER)
# 爬取数据
def crawling_data():
# 动态加载的太多,多滚动几次
for i in range(3):
# 等待底部翻页栏加载完
wait.until(EC.presence_of_element_located((By.ID, 'J_bottomPage')))
# 滚动条滚到底部
browser.execute_script('window.scrollTo(0,10000)') #document.body.scrollHeight
# 发现如果没加载完的话有个id为J_scroll_loading的div
# 等待加载元素消失
wait.until_not(EC.presence_of_element_located((By.ID,'J_scroll_loading')))
time.sleep(1)
page_count=wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'#J_bottomPage .p-skip b')))
page_input = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#J_bottomPage .p-skip .input-txt')))
page_button = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, '#J_bottomPage .p-skip a')))
#解析并存储
for item in parse_jd_html(browser.page_source):
write_to_file(item,keys+'.txt')
count=int(page_count.text)
current=int(page_input.get_attribute('value'))
print('完成:',current,'/',count)
# 爬下一页
if count>current:
page_input.clear()
page_input.send_keys(str(current+1))
#print(current+1)
page_button.click()
crawling_data()
crawling_data()
except Exception as e:
print('exception:',e)
else:
print('success')
finally:
#close只会关闭当前页面,quit会退出驱动并且关闭所关联的所有窗口,最后执行完以后就关闭。
browser.quit()
print('browser quit')
if __name__ == '__main__':
search_by_keys('C++书籍')
爬取结果:
Over,不得不说selenium真是个好东西~~