首页 > 代码库 > selenium+python 爬取网络图片(2) -- 百度

selenium+python 爬取网络图片(2) -- 百度

上一篇博文介绍了如何用selenium+python在诸如soso、谷歌、好搜等搜索引擎上爬取图片的方法,但是却没用提到百度,因为百度的情况比较特殊。首先,百度图片的数据更好,因为每幅图片都有“data-desc”描述可以作为图像很好的语义标签,此外基于百度较强的技术其查询搜索得到的图片相关性较高,后续人工筛选工作较少;其次,百度图片的数据不容易爬取,如果像前一篇文章中的方法取img标签的src值作为下载url,是下载不到图片的,得到的知识167B的非图像数据。


那么,如何爬取百度图片呢,笔者尝试了两种方法。第一种方法尚未完整实现,但思路已完整,第二种方法可以较为简单的爬到百度图片数据源。下面依次介绍两种实现方案。


方案1:

使用selenium模拟鼠标操作--“将鼠标放置图像上方,右键并选择图像另存为选项”,然后就可以保存了,代码如下:

from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys

# init
url = 'http://image.baidu.com/i?tn=baiduimage&ipn=r&ct=201326592&cl=2&lm=-1&st=-1&fm=index&fr=&sf=1&fmq=&pv=&ic=0&nc=1&z=&se=1&showtab=0&fb=0&width=&height=&face=0&istype=2&ie=utf-8&word=%E6%89%8B%E6%9C%BA&oq=shouji&rsp=1'
xpath = '//ul/li/div/a/img'

# set profile
fp = webdriver.FirefoxProfile()
fp.set_preference('browser.download.folderList', 2)
fp.set_preference('browser.download.manager.showWhenStarting', False)
fp.set_preference('browser.download.dir', './yourfolder/')
fp.set_preference('browser.helperApps.neverAsk.saveToDisk', 'image/jpeg')

# launch driver
driver = webdriver.Firefox(firefox_profile=fp)
driver.maximize_window()
driver.get(url)

for element in driver.find_elements_by_xpath(xpath):
    img_url = element.get_attribute('src')
    img_desc = element.get_attribute('data-desc')

    action = ActionChains(driver).move_to_element(element)
    action.context_click(element)
    action.send_keys(Keys.ARROW_DOWN)                
    action.send_keys('v')
    action.perform()
    # click save image

driver.close()
但是,想必大家都会发现,保存图片还需要一次次点击对话框的确认保存,很繁琐。的确,为了解决这个问题,我google了好久并没有找到直接解决的好方法,根本原因是selenium无法操作操作系统级的对话框,有说上面“set profile”代码段的设置能解决问题的并不靠谱。所以,如果采用右键另存为的方案的话,需要额外使用插件或钩子程序模拟自动点击。网上有推荐一个AutoIT的或可完成任务,未亲试。


方案2:

百度图片img标签内含的src并不能下载到原图片,只有data-desc属性可用,但是,当鼠标放在百度图片上时,会发现如下图所示的下载按钮

技术分享

只要找到这个上面这个下载按钮对应的链接即可下载到原图,而按钮对应的则是一个a链接标签,分析出其xpath问题即解决了,下面给出python代码:

import urllib
import time
from selenium import webdriver

class Crawler:

    def __init__(self):
        self.url = 'http://image.baidu.com/i?tn=baiduimage&ipn=r&ct=201326592&cl=2&lm=-1&st=-1&fm=index&fr=&sf=1&fmq=&pv=&ic=0&nc=1&z=&se=1&showtab=0&fb=0&width=&height=&face=0&istype=2&ie=utf-8&word=%E6%89%8B%E6%9C%BA&oq=shouji&rsp=1' # url to crawl
        self.img_xpath = '//ul/li/div/a/img' # xpath of img element
        self.download_xpath = '//ul/li/div/div/span/a[@class="downloadicon"]' # xpath of download link element
        self.img_url_dic = {}

    # kernel function
    def launch(self):
        # launch driver
        driver = webdriver.Firefox()
        driver.maximize_window()
        driver.get(self.url)

        img_xpath = self.img_xpath
        download_xpath = self.download_xpath
        img_url_dic = self.img_url_dic
        
        # 模拟滚动窗口以浏览下载更多图片  
        pos = 0   
        for i in range(10):  
            pos += i*500 # 每次下滚500  
            js = "document.documentElement.scrollTop=%d" % pos  
            driver.execute_script(js)
            # get image desc and download
            for img_element, link_element in zip(driver.find_elements_by_xpath(img_xpath), driver.find_elements_by_xpath(download_xpath)):
                img_desc = img_element.get_attribute('data-desc') # description of image
                img_desc = self.filter_filename_str(img_desc)
                
                img_url = link_element.get_attribute('href') # url of source image
                if img_url != None and not img_url_dic.has_key(img_url):  
                    img_url_dic[img_url] = '' 
                    ext = img_url.split('.')[-1]
                    filename = img_desc + '.' + ext
                    print img_desc, img_url
                    urllib.urlretrieve(img_url, './yourfolder/%s' % filename)
                    time.sleep(1)
        driver.close()

    # filter invalid characters in filename
    def filter_filename_str(self, s):
        invalid_set = ('\\','/',':','*','?','"','<','>','|',' ')
        for i in invalid_set:
            s = s.replace(i, '_')
        return s    

if __name__ == '__main__':
    crawler = Crawler()
    crawler.launch()
爬取后的结果图如下所示:

技术分享


以上代码仅示例的实现了方案,验证其可行性,内部可能含有部分疏漏,仅供需要的朋友参考,不妥之处请指正。







selenium+python 爬取网络图片(2) -- 百度