使用selenium实现简单网络爬虫抓取MM图片
撸主听说有个网站叫他趣,里面有个社区,其中有一项叫他趣girl,撸主点进去看了下,还真不错啊,图文并茂,宅男们自己去看看就知道啦~
接下来当然就是爬取这些妹子的图片啦,不仅仅是图片,撸主发现里面的对话也很有意思,于是把对话也一并抓取下来好了。
那么问题来了,用什么工具呢?在之前的练习中已经用过urllib2,正则表达式匹配实在麻烦,这次来点稍微高级的,试试selenium;
selenium是什么?其实它是一个web自动化测试的工具,运行起来就跟我们自己操作浏览器差不多,废话不多说,下面开始。
工具:python2.7 + selenium2 python版
1 首先导入需要的模块,然后定义本地存放目录
1 # coding: utf-8 2 3 from selenium import webdriver 4 from selenium.common.exceptions import NoSuchElementException 5 from time import * 6 import os 7 import urllib2 8 9 # 文件保存路径 10 file_path = r'F:\taqu'
2 然后还是定义3个函数,分别用来为每个girl创建目录,保存每个girl的图片,保存每个girl的文本描述和对话;其中在保存图片的时候还是用到了urllib2
# ------------定义3个函数,用来创建每个girl的目录、保存图片、写入文本描述及对话---------------- def mkdir_for_girl(path, name): """ 创建以标题命令的目录 :param name: 目录名称 :return: 返回创建的目录路径 """ path = os.path.join(path, name) if not os.path.exists(path): os.mkdir(path) return path def save_pictures(path, url_list): """ 保存图片到本地指定文件夹 :param path: 保存图片的文件夹,由mkdir_for_girl返回 :param url_list: 待保存图片的url列表 :return: none """ for (index, url) in enumerate(url_list): try: print u'%s 正在保存第%d张图片' % (ctime(), index) pic_name = str(index) + '.jpg' file_name = os.path.join(path, pic_name) # 如果存在该图片则不保存 if os.path.exists(file_name): print u'%s 该图片已经存在' % ctime() continue req = urllib2.Request(url, headers={'User-Agent': r'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:45.0) Gecko/20100101 Firefox/45.0'}) data = urllib2.urlopen(req, timeout=30).read() f = open(file_name, 'wb') f.write(data) f.close() except Exception, e: print u'%s 第%d张图片保存失败,不处理,跳过继续处理下一张' % (ctime(), index) def write_text(path, info): """ 在path目录中创建txt文件,将info信息(girl的文本描述和对话)写入txt文件中 :param path: 保存txt文件的目录,由mkdir_for_girl返回 :param info: 要写入txt的文本内容 :return: none """ # 创建/打开info.txt文件,并写入内容 filename = os.path.join(path, 'info.txt') with open(filename, 'a+') as fp: fp.write(info.encode('utf-8')) fp.write('\n'.encode('utf-8')) fp.write('\n'.encode('utf-8'))
3 定义webdirver,打开目标网页,其中切换到目标页面使用了超链接文本定位元素
# -----------------------打开页面,设置超时时间,窗口最大化----------------------- driver = webdriver.Firefox() driver.implicitly_wait(10) driver.maximize_window() driver.get(r'http://www.taqu.cn/community/') # -----------------------切换到“他趣Girl”页面----------------------- driver.find_element_by_partial_link_text(u'他趣Girl').click()
4 由于所有girl都在一个页面中,但是这个页面很长很长,真的很长,需要滑动很久才能滑到底部,为什么要滑动到底部呢?
因为浏览器获取的HTML源码,只有当前窗口已显示元素的代码,未显示部分的代码没有,当然selenium也定位不到那些元素了;
所以说,如果要selenium能找到所有girl,先要把页面滑动到底部,等所有girl都加载完,才能获取到包含所有girl的HTML代码。
那么问题来了,怎么滑动页面呢,通过浏览器的滚动条,但是滚动条不是HTML元素,selenium不能直接控制,这里就得借助
javascript了,在selenium中执行js代码还是可以的,具体实现看代码:
# -----------------------滑动到窗口最底部,以将所有的girl都刷新出来----------------------- # 由于页面很长,并且需要不断下拉才会刷新,因此通过javascript来控制器滚动条向下滑动 # 但是一次滑动并不能到达底部,需要多次,那么需要多少次呢?这里采用的方式是不停的向下 # 滑动,每滑动一次,都查询下是否到达底部,怎么查询呢?这是通过查到底部的一个标志图片来判断, # 如果没找到标志,就说明还没到达底部,需要继续滑动,如果找到就跳出循环
# 为了快速滑动,先设置超时时间为1秒
driver.implicitly_wait(1)
# 不停的滑啊滑 while True: driver.execute_script("window.scrollTo(0,document.body.scrollHeight)") try: # 定位页面底部的一个图片 driver.find_element_by_xpath(".//*[@id='waterfall-loading']/img[@src='/img/no-more.png']") # 如果没抛出异常就说明找到了底部标志,跳出循环 break except NoSuchElementException as e: # 抛出异常说明没找到底部标志,继续向下滑动 pass # 将超时时间改回10秒 driver.implicitly_wait(10)
5 获取到整个完整的页面后,就可以一次找出所有的girl封面图片了,这些封面图片是可以点击的,一点就打开了该girl的所有图片和对话,
这里使用的是CSS选择器,比起xpath,撸主更喜欢CSS一些
# -----------------------找到所有girl的封面图片----------------------- # 这些封面图片是可点击的,单击都会弹出该girl的所有图片和文字描述 girls = driver.find_elements_by_css_selector("div#container img") num = len(girls) print u"girl总数为:%d" % num
6 最后一步了,遍历所有的封面图片,依次点击,然后抓取保存每一个girl的图片和对话,保存到本地
# -----------------------依次点击每张封面,提取每个girl的信息----------------------- for girl in girls: # 依次点击每一个girl的封面 girl.click() # 每点击一个girl后,都再点击一下弹出框,以更新driver,不然driver中的缓存还是上一个girl的 # 一定要注意这一步啊,撸主当时没有做这一步,折腾了好久 driver.find_element_by_xpath("html/body/div[3]/div[2]").click() # 提取标题,由于标题中的字符:和|不能作为文件名,将他们替换了 title = driver.find_element_by_css_selector("p.waterfall-detail-header-title").text title = title.encode('utf-8') title = title.replace(":", ":") title = title.replace("|", "丨") title = title.decode('utf-8') # 在file_path目录下,为该girl创建以标题命名的目录 path = mkdir_for_girl(file_path, title) # 提取该girl所有图片的URL pics = driver.find_elements_by_css_selector("div.water-detail-content img") pic_url = [x.get_attribute('src') for x in pics] print u'该girl的图片总数为:%d' % len(pic_url) # 保存图片到本地以该girl标题命名的目录中 save_pictures(path, pic_url) # 提取girl的基本介绍,并写入info.txt文件 info = driver.find_element_by_xpath("html/body/div[3]/div[2]/div[2]/div[2]").text write_text(path, info) # 提取所有对话,写入info.txt文件 talks = driver.find_elements_by_css_selector("div.water-detail-content p") for t in talks: write_text(path, t.text) # 关闭该girl的图片 driver.find_element_by_xpath("html/body/div[3]/div[2]/div[1]/div/img").click() print u'该girl信息提取完成,继续处理下一个' sleep(1) # -----------------------所有girl信息提取完成----------------------- driver.close() print u'恭喜,所有girl信息提取完成!'
7 试着运行下,看下输出,是不是有点小激动
girl总数为:90 该girl的图片总数为:12 Sat May 14 09:24:54 2016 正在保存第0张图片 Sat May 14 09:24:54 2016 正在保存第1张图片 Sat May 14 09:24:54 2016 正在保存第2张图片 。 。省略很多 。 Sat May 14 09:33:36 2016 正在保存第9张图片 Sat May 14 09:33:37 2016 正在保存第10张图片 Sat May 14 09:33:37 2016 正在保存第11张图片 该girl信息提取完成,继续处理下一个 恭喜,所有girl信息提取完成!
8 再看下本地目录,是不是很激动?
说明:上面的代码是完整的,将所有的分段合并在一起就可以运行了。这里可以看到,selenium是很强大的,不仅仅是web自动化工具哦,
还是爬虫利器呢;但是,但是这里有个缺点就是,selenium会打开浏览器UI,这样操作起来就有点偏慢了,有没有更好的解决方案呢?---有的!