收藏笔记我爱编程Python

Python3+requests_html+selenium爬取

2018-05-20  本文已影响385人  SamBrother

开篇强调一遍:本文仅仅为了技术交流而作,请各位不要用于商业目的,切记切记!!!


作为一只撸代码的IT民工,经常撸代码撸得的头昏眼花,这个时候就非常需要点什么东西刺激一下自己,当然,对于死宅男的程序猿没有什么是比美女更能让我们性奋的了,呃。。。是兴奋。。。。找美女图片看还得是高清无码的,确实难找,那么我们就将就点撸个代码爬取点妹子图下来对着屏幕慢慢撸...代码(别想多了,骚年)。下面开始秀了,请各位睁大眼睛,竖起耳朵....

今天为大家提供的是解决方案是requests_html+selenium爬取妹子图的妹子,点击妹子图网址出现的界面是这样的:
满屏的妹子
是不是看着很激动,别急,骚年,马上传授你python大法打通你任督二脉收了这些妹子。
首先我们分析下这个网站的特点,当你点击 www.mmjpg.com的时候就会出现上图所示的页面,接着你再点击“下一页”,它就会跳转到http://www.mmjpg.com/home/2的页面,没错,还是满屏的妹子,但是,(请注意,敲黑板,划重点)这个时候你再把2改成1,你期望的是肯定跳转到首页啦,小伙子我很看好你但是结果它就不是跳转到第一页,它跳转到下图所示的界面了,what fuck!!这是什么鬼东西!!我裤子都脱了,你竟然给我看这个。。。。没错这个就是传说中的404页面!!!
404页面
通过分析我们发现这个网站的第一页是www.mmjpg.com,后面的页面格式为http://www.mmjpg.com/home/n ,n是页码。而且每页有15个妹子,下面页码显示当前共有91页可以爬取。

当然,今天我们不根据下面显示的页码爬取,我们按导航条的类别分类爬取,并按导航的分类新建文件夹分类保存。
导航条
我们的步骤是:

我们的开发环境是win7+python3.6,使用的是最新的requests_html库,该库是由requests 作者 kennethreitz 出的一个新库 requests-html,它可以用来解析 HTML文档。requests-html是基于现有的框架 PyQuery、Requests、lxml 等库进行了二次封装,更加方便开发者调用。
至于为什么选择Python3.6而不是最新版的Python大法是因为…因为这个requests-html目前只支持Python3.6,其他的Python版本均无法正常运行...


俗话说“战马关公身上纹,掌声送给社会人”,下面有请小猪佩奇同学登场镇楼...(额...貌似现在这只猪比较Fashion...)


整个爬取的代码我们封装成一个类。First,请下载:
pip install requests-html
pip install selenium
class SpiderMM:
    def __init__(self):
        self.start_page = 2
        self.all_sort_urls = []
    def mkdir(self, file_name):
        ...
    def get_all_sort_urls(self):
        ...
    def get_all_girls(self):
        ...
    def get_one_sort_girls(self, urls, title):
        ...
    def more_threads(self, urls, path):
        ...
    def save_img(self, path, urls):
        ...
    def run(self):
        ...
if __name__=='__main__':
    spider = SpiderMM()
    start_time = time.ctime(time.time())
    spider.run()
    end_time = time.ctime(time.time())
    cost_time=end_time-start_time
    print('耗时:%-5s 秒' % (cost_time))
    from requests_html import HTMLSession
    ...
    def get_all_sort_urls(self):
        # 首页基网址
        base_url = "http://www.mmjpg.com/"
        self.session = HTMLSession()
        result = self.session.get(base_url, headers=headers, timeout=15)
        if result.status_code == 200:
            self.all_sort_urls = result.html.find('div.subnav > a')
            print(self.all_sort_urls)
        else:
            print('Sorry!!该页面可能不在地球服务器上....')

这样所有分类的urls我们就全部获取了,我们选中性感标签点击进入跳转页面,出现如下图页面:


果然很...性感...

再次点击电脑的F12按键调出开发者工具(注意力不要放在妹子上!!!)出现如下图界面:

我们关心的是图中红色框框中的信息,即每个妹子的titleurl。继续分析该页面url发现点击底部页面数字时网址的地址变成[http://www.mmjpg.com/tag/xinggan/2],而该页面的首页基网址为[http://www.mmjpg.com/tag/xinggan],原来除了第一页的网址格式不同外其余的网址都遵循http://www.mmjpg.com/tag/xinggan/n的格式,其中n就是底部页码导航显示的数字,那这样只要我们知道该分类总页码就可以通过迭代出该分类中所有妹子的urls了。那怎么获取总页码数呢?
看到上图中下面那个红色框框没,对,那里就是我们要找的key。找到了信息点要怎么获取这个信息呢?别急,我们除了python大法外还有把大保健(宝剑),就是正则表达式,利用这把保健我们可以轻易的获取页码数,只需一句end_page = int(re.compile(r'\d+').search(item.text).group())即可轻松过滤除总页数,是不是 so easy?!

因为每个分类的首页与第N页的网址格式有差别,故我们在程序中对每个分类的首页进行单独的解析,同时将解析得到的每个妹子url都通过append函数追加到one_sort_urls = []中,即one_sort_urls.append(url),这样每个分类的妹子信息urls我们都获取了。
def get_all_girls()代码如下:
    def get_all_girls(self):
        self.get_all_sort_urls()
        for item in self.all_sort_urls:
            base_page_url = item.attrs['href']
            print(base_page_url)  # http://www.mmjpg.com/tag/xinggan
            sort_title = item.text
            print(sort_title)

            # 第一页
            first_page_result = self.session.get(base_page_url, headers=headers, timeout=15)
            if first_page_result.status_code == 200:
                for item in first_page_result.html.find('div.page > em.info'):
                    end_page = int(re.compile(r'\d+').search(item.text).group())
                    print('共%s页' % end_page)
                    one_sort_urls = []
                    for item in first_page_result.html.find('div.pic> ul > li > span.title > a'):
                        title = item.text
                        url = item.attrs['href']
                        print('title:%s\nurl:%s' % (title, url))
                        one_sort_urls.append(url)

            # 第二页开始到最后页
            for i in range(self.start_page, (end_page + 1)):
                print("迭代第%i页" % i)
                result = self.session.get(base_page_url + '/' + str(i), headers=headers, timeout=15)
                if result.status_code == 200:
                    for item in result.html.find('div.pic> ul > li > span.title > a'):
                        title = item.text
                        url = item.attrs['href']
                        print('title:%s\nurl:%s' % (title, url))
                        one_sort_urls.append(url)
            print(one_sort_urls)
            self.get_one_sort_girls(one_sort_urls, sort_title)

配置webdriver
# for IE
ie_options = webdriver.IeOptions()
ie_options.add_argument("--headless")
ie_options.add_additional_option("prefs", prefs)
ie_path = 'D:\Python\IEDriverServer'

接下来我们就可以在程序中通过selenium模拟点击全部图片加载该妹子所有图片的urls,未点击该按钮时通过F12键可以看到下图信息:


点击后如下图所示:

是不是多出好多urls,这些就是该妹子所有图片的urls。获取了每个妹子的urls我们就可以在本地通过分类+妹子的格式创建一个文件夹保存接下来要download的妹子图片了。创建文件的代码如下:
    def mkdir(self, file_name):
        path = "F:\\xxoo\\MM\\" + file_name
        folder = os.path.exists(path)
        if not folder:
            os.makedirs(path)
            print('%s 创建成功!!' % path)
        else:
            print('%s 已经存在!!' % path)
            pass
        return path

有人要问你为啥要返回文件夹路径呢?因为接下来的save_img方法中要用到这个path
至于怎么爬,看下面代码:

    def get_one_sort_girls(self, urls, title):
        # driver = webdriver.Chrome(chrome_path, chrome_options=chrome_options)
        driver = webdriver.Ie(ie_path, ie_options=ie_options)
        for url in urls:
            print('url-------->:%s' % url)
            # result = self.session.get(url, headers=self.headers, timeout=15)   
            driver.get(url, headers=self.headers, timeout=15)
  )
            # time.sleep(3)
            driver.implicitly_wait(5)  # let explore wait some seconds
            driver.find_element_by_xpath('//*[@id="opic"]').click()
            driver.implicitly_wait(5)
            # time.sleep(10)
            html = driver.page_source
            selector = etree.HTML(html)
            all_girl_urls = selector.xpath('//div[@id="content"]/img/@data-img')
            girl_name = selector.xpath('//div[@class="article"]/h2/text()')[0]
            print('girl_urls:%s\ngirl_name:%s' % (all_girl_urls, girl_name))
            sub_img_path = title + "\\" + girl_name
            path = self.mkdir(sub_img_path)
            self.more_threads(all_girl_urls, path)

        driver.close()  # close explore

可能有眼尖的骚年已经发现上面的方法中嵌套了一个方法self.more_threads(all_girl_urls, path),对,这个就一个多线程下载的方法,使用多线程主要是这个网站的图片太多了,要是单线程下载不知道下载到猴年马月了,至于多线程自己百度了解下如何使用,这里我开启了4条线程下载,给出代码参考如下:

    # 多线程下载
    def more_threads(self, urls, path):
        num = len(urls) // 4
        print('共有%i张图片' % (len(urls)))
        a = []
        i = 1
        for url in urls:
            temp = [i, url]
            i += 1
            a.append(temp)

        list1 = a[0:num]
        list2 = a[num:2 * num]
        list3 = a[2 * num:3 * num]
        list4 = a[3 * num:4 * num]

        threads = []
        t1 = threading.Thread(target=self.save_img, args=(path, list1,))
        threads.append(t1)
        t2 = threading.Thread(target=self.save_img, args=(path, list2,))
        threads.append(t2)
        t3 = threading.Thread(target=self.save_img, args=(path, list3,))
        threads.append(t3)
        t4 = threading.Thread(target=self.save_img, args=(path, list4,))
        threads.append(t4)

        for t in threads:
            t.setDaemon(True)
            t.start()
        for t in threads:
            t.join()

这个方法传入的两个参数,一个是妹子的urls,另一个是图片保存的路径。


好,接下来我们聊聊如何保存妹子图片,保存就不想多说了,直接贴代码:

    def save_img(self, path, urls):
        for i, url in urls:
            try:
                img_response = requests.get(url, headers=headers, timeout=15)
                with open(path + '\\' + str(i) + '.jpg', 'wb') as file:
                    file.write(img_response.content)
                    print("第%i张图片保存成功!" % (i))
            except Exception as e:
                print(e)

到这里很多小伙伴一定很激动了,所有的工作都做完了只要写个执行方法点击执行就OK了,好,给出run方法让你happy下:

    def run(self):
        self.get_all_girls()

接下来就是见证妹子的时刻了,请准备好你们的纸巾,睁大你们的眼睛,一大波妹子正在来的路上额....



当你执行完程序后迫不及待的到本地目录下想一睹妹子的盛世容颜时,结果....你会发现文件夹中都是下面这张图:

What fuck ???!!


这是什么鬼,我的程序日志显示正常下载保存了啊?!!怎么会出现这种情况??
别急,这是因为这个网站在你请求的时候处理过请求头headers,爬虫就是模拟人浏览网页来获取数据,那么既然不想被反爬虫识别出来,那么我们的爬虫就要更像人一样浏览网页。通过分析我们发现这个妹子图的请求图有个猫腻,如下图红色框框中所示:
Headers
发现了没?这个请求头中有个Referer,接下来我们只需要将这个Referer加入到我们的请求
headers = {
    'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) '
                  'Chrome/65.0.3325.181 Safari/537.36',
    'Referer': "http://www.mmjpg.com"
}

好,接下来再运行下程序,你会发现你设置download的目录下会出现各个分类的文件夹,各个分类文件夹下又有不同妹子命名的子文件夹,各个子文件夹下就是各个妹子的全部图片了。好,大功告成!!小伙伴们请准备好你们的纸巾继续撸代码吧,注意:

最后再强调一遍:本文仅仅为了技术交流而作,请各位不要用于商业目的,切记切记!!!

上一篇 下一篇

猜你喜欢

热点阅读