使用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,这样操作起来就有点偏慢了,有没有更好的解决方案呢?---有的!

posted @ 2016-05-14 11:20  oceany  阅读(11703)  评论(2编辑  收藏  举报