Selenium 使用实例,爬取图片、浏览器截长图
Selenium 使用实例
翻页爬取图片
-
需要用到的模块
from selenium import webdriver #浏览器驱动 from selenium.webdriver.chrome.options import Options #设置 # 其他模块 import time import ssl import threading #多线程下载 import urllib.request #下载 import urllib.error #下载
-
携带配置、创建浏览器。
def browser_get(): # 创建设置对象 chrome_options = Options() # 向对象添加参数,手机模式。 chrome_options.add_experimental_option("mobileEmulation", { "deviceName": "Nexus 5" }) # 向设置添加headless模式。 chrome_options.add_argument('--headless') # 设置驱动位置、传入设置对象 browser = webdriver.Chrome(r'F:\study_project\webpack\SeleniumDemo\chromedriver.exe',chrome_options = chrome_options) # 打开url browser.get('http://jandan.net/ooxx') # 设置等待时间。 browser.implicitly_wait(10)
-
重要 设置页面滚动参数,让页面加载完全才能取得到xpath。
def browser_get(): ... ... js="document.documentElement.scrollTop="+ str(500) browser.execute_script(js) time.sleep(2)
-
取到资源列表。这里最好直接取到全部目标元素。否则会出现意外bug。
download_pic
是下载方法,给他传入图像url就能实现下载。def browser_get(): ... ... # 假设这些元素携带了图片url地址。 ul = browser.find_elements_by_xpath('//a[@class="view_img_link"]') # 我们采用多线程来下载图片。 for li in ul: threading.Thread(target=download_pic,args=(li.get_attribute("href"),)).start() # 我们第一页内容已经下载完了,我们需要进入循环来下载页面内容了。下载完第一页,执行request_start函数。 request_start(browser,base_url)
-
进入翻页循环。
def request_start(browser,base_url): # 我们在每页都需要滚动页面到下面来完成js渲染。 js="document.documentElement.scrollTop="+ str(8000) browser.execute_script(js) time.sleep(2) browser.implicitly_wait(10) # 在这里找到翻页元素调用其点击。 element=browser.find_element_by_xpath('//a[@title="Older Comments"][1]') print('text--------{}'.format(element.text)) if element: # 如果存在就点击下一页。给一个反应时间。慢一点... element.click() time.sleep(10) # 和上面一样,继续找到页面所有携带图片url的元素 ul = browser.find_elements_by_xpath('//a[@class="view_img_link"]') for li in ul: threading.Thread(target=download_pic,args=(li.get_attribute("href"),)).start() if ul: #如果ul存在就不停的爬取。 return request_start(browser,base_url)
-
下载图片的方法
# 下载图片 def download_pic(url): correct_url = url if url.startswith('//'): correct_url = url[2:] if not url.startswith('http'): correct_url = 'http://' + correct_url print(correct_url) headers = { 'Host': 'wx2.sinaimg.cn', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' 'Chrome/61.0.3163.100 Safari/537.36 ' } try: req = urllib.request.Request(correct_url, headers=headers) resp = urllib.request.urlopen(req) pic = resp.read() pic_name = correct_url.split("/")[-1] with open(pic_save_path + pic_name, "wb+") as f: f.write(pic) except (OSError, urllib.error.HTTPError, urllib.error.URLError, Exception) as reason: print(str(reason))
-
设置ssl上下文、定义主函数。
if __name__ == "__main__": ssl._create_default_https_context = ssl._create_unverified_context browser_get()
-
总结,用到的关键语句。
browser .implicitly_wait(10) #设置等待超时 .find_elements_by_xpath() #返回列表 .find_element_by_xpath() #返回单个 .execute_script(js) #执行js .add_experimental_option() #添加设置 js="document.documentElement.scrollTop="+ str(500) #页面滚动的js语句。 threading.Thread(target=fun,args=(**kwargs,)).start() #多线程
滚动截取网页
- 首先要知道如何进行页面滚动。例如我们用手机模式访问站点。那么我们需要知道手机高度
pix_h=640
- 向浏览器发送js可执行脚本把滚动条下拉64,
js="document.documentElement.scrollTop="+ str(i*pix_h)
- 截图并获取滚动条当前状态。
"return document.documentElement.scrollHeight.toString()+','+document.documentElement.scrollTop.toString()"
- 判断底部高度是否没有变化,没有证明到底了。有就继续执行操作2。
- 对于滚动加载的页面需要判断是否需要再次加载。这时就要等待一下继续判断。如果仍没有,就的确到底了。还要给循环加上最大次数。
-
引用的包
# selenium from selenium import webdriver from selenium.webdriver.chrome.options import Options #一些系统和文件操作用于合并 import time import os from PIL import Image
-
封装这个函数。有些页面你需要点确定/关闭才能浏览,我们需要操作一下让浏览器自动点确定,只需要输入关闭按钮xpath
notify
。其中一部分准备。真正的循环结构下面说明。def url_captrue(url_path='https://guazi.com/bj/buy/',pix_h=640,headless=True,deviceName="Nexus 5",exePath='F:\study_project\webpack\SeleniumDemo\chromedriver.exe',notify='/html/body/div[5]/div[2]/div/img'): """垂直合并多张图片 url_path - 打开的页面 pix_h - 手机高度。 headless - 是否使用无图模式 deviceName - 如果是手机,请设置设备名称。如果是浏览器请写deviceName='' exePath - 驱动文件位置。 notify - 首页是否有通知,默认有。如无填写notify='' """ #创建浏览器设置 chrome_options = Options() headlessflag=lambda x: '--headless' if x==True else '' if headless: chrome_options.add_argument(headlessflag(headless)) #添加设备参数 if deviceName!='' or deviceName!="" or deviceName!=None: chrome_options.add_experimental_option("mobileEmulation", { "deviceName": deviceName }) #启动浏览器 wd = webdriver.Chrome(exePath,chrome_options = chrome_options) wd.get(url_path) #检测首页通知参数 wd.implicitly_wait(10) if notify!='' or notify!="" or notify!=None: wd.find_element_by_xpath(notify).click()
-
循环结构。这就刚才提到的几步。
def url_captrue(url_path='https://guazi.com/bj/buy/',pix_h=640,headless=True,deviceName="Nexus 5",exePath='F:\study_project\webpack\SeleniumDemo\chromedriver.exe',notify='/html/body/div[5]/div[2]/div/img'): ... ...(继承上面代码) i=0 img_list = [] while i<8: js="document.documentElement.scrollTop="+ str(i*pix_h)+";" wd.execute_script(js) js1 = "return document.documentElement.scrollHeight.toString()+','+document.documentElement.scrollTop.toString()" js1_result = wd.execute_script(js1) real_scroll_h, real_top = js1_result.split(',')[0], js1_result.split(',')[1] print('{}-----{}'.format(real_scroll_h,real_top)) if real_top == str(i*pix_h): # 当前滚动条位置是都等于预期位置。是就截图 i += 1 #wd.save_screenshot('static/capture/wap/wap-'+filename + str(i)+'.png') wd.get_screenshot_as_file('imgcontent\{}.png'.format(i)) img_list.append('imgcontent\{}.png'.format(i)) last_t = real_top else: # 如果本次设置失败,看这次的top和上一次记录的top值是否相等,相等则说明没有新加载内容,且已到页面底,跳出循环 if real_top != last_t: # 当前滚动条不等于本次预期位置。也不是上次预期位置,进入下次循环截图。 last_t = real_top time.sleep(5) # 这里需要等待一下,对于滚动页面。看看还有没有加载。 print('加载一次') else: # 当前滚动条不等于本次预期位置,而是上次预期位置。直接退出 #wd.get_screenshot_as_file('imgcontent\{}.png'.format(i)) #img_list.append('imgcontent\{}.png'.format(i)) break image_merge(img_list, r'F:\study_project\webpack\SeleniumDemo\02SeleniumHeaderLess', 'all.png')
-
合并图像的代码:
def image_merge(images, output_dir, output_name='merge.jpg', restriction_max_width=None, restriction_max_height=None): """垂直合并多张图片 images - 要合并的图片路径列表 ouput_dir - 输出路径 output_name - 输出文件名 restriction_max_width - 限制合并后的图片最大宽度,如果超过将等比缩小 restriction_max_height - 限制合并后的图片最大高度,如果超过将等比缩小 """ def image_resize(img, size=(1500, 1100)): """调整图片大小 """ try: if img.mode not in ('L', 'RGB'): img = img.convert('RGB') img = img.resize(size) except Exception(e): pass return img max_width = 0 total_height = 0 # 计算合成后图片的宽度(以最宽的为准)和高度 for img_path in images: if os.path.exists(img_path): img = Image.open(img_path) width, height = img.size if width > max_width: max_width = width total_height += height # 产生一张空白图 new_img = Image.new('RGB', (max_width, total_height), 255) # 合并 x = y = 0 for img_path in images: if os.path.exists(img_path): img = Image.open(img_path) width, height = img.size new_img.paste(img, (x, y)) y += height if restriction_max_width and max_width >= restriction_max_width: # 如果宽带超过限制 # 等比例缩小 ratio = restriction_max_height / float(max_width) max_width = restriction_max_width total_height = int(total_height * ratio) new_img = image_resize(new_img, size=(max_width, total_height)) if restriction_max_height and total_height >= restriction_max_height: # 如果高度超过限制 # 等比例缩小 ratio = restriction_max_height / float(total_height) max_width = int(max_width * ratio) total_height = restriction_max_height new_img = image_resize(new_img, size=(max_width, total_height)) if not os.path.exists(output_dir): os.makedirs(output_dir) save_path = '%s/%s' % (output_dir, output_name) new_img.save(save_path) return save_path
-
编写主函数测试一下
if __name__ == "__main__": url_captrue()