zoukankan      html  css  js  c++  java
  • 爬虫实例——爬取淘女郎的相册(借助谷歌浏览器的开发者工具找出规律快速爬取)

    用正常的方式(selenium、PhantomJS、BeautifulSoup)爬取淘女郎相册不仅困难,效率很低,而且很容易卡死。

    我借助谷歌浏览器的开发者工具找出每个页面的规律,快速获取每张照片的链接,再下载,这样效率就很高了。

    过程

    首页很简单,没有采用JS渲染,直接用requests就能获取完整的源代码,没什么说的。

    淘女郎首页采用了JS渲染,直接用requests是获取不到完整的源代码的,此时可以打开谷歌浏览器的开发者工具,主要看“Network”,筛选出“XHR”,如下图:

    从上图可知,从这个页面可以获取淘女郎的个人资料,读者可以直接从浏览器中打开该链接,看看是否能获取淘女郎的个人资料。

    https://mm.taobao.com/self/info/model_info_show.htm?user_id=687471686

    观察这个URL很容易发现,我们只需改变用户ID就可以得到不同淘女郎的个人资料了,所以在爬取首页时记得把用户ID也爬取下来。

    接下来看一下相册,打开淘女郎的相册,同样是打开谷歌浏览器的开发者工具,看“XHR”,你可以得到这么一个链接:

    https://mm.taobao.com/self/album/open_album_list.htm?_charset=utf-8&user_id%20=687471686

    从这个链接你可以获取淘女郎的相册链接、名称和照片数,但得到的只是第一页的数据,怎么看第二页的呢,在前台翻页后从开发者工具可以看到多了一个XHR:

    https://mm.taobao.com/self/album/open_album_list.htm?_charset=utf-8&user_id%20=687471686&page=2

    这就是相册的规律,通过两个参数控制,一个是用户ID,一个是页数。

    接下来看看相册里面的内容,此时看XHR得不到我们要的内容,应该看看别的,我在JS里发现了这个页面:

    从上图可知,这个页面会返回JSON数据,里面包含了图片的链接,但通过查找发现只有16张照片,实际上这个相册却有45张照片,回到前台,下拉滚动条,发现多了几个这样的页面:

    它们的URL分别是:

    https://mm.taobao.com/album/json/get_album_photo_list.htm?user_id=687471686&album_id=10000702574&top_pic_id=0&cover=%2F%2Fimg.alicdn.com%2Fimgextra%2Fi2%2F687471686%2FTB1TlwDLFXXXXbxaXXXXXXXXXXX_!!2-tstar.png&page=1&_ksTS=1465185952899_155&callback=jsonp156

    https://mm.taobao.com/album/json/get_album_photo_list.htm?user_id=687471686&album_id=10000702574&top_pic_id=0&cover=%2F%2Fimg.alicdn.com%2Fimgextra%2Fi2%2F687471686%2FTB1TlwDLFXXXXbxaXXXXXXXXXXX_!!2-tstar.png&page=2&_ksTS=1465186271282_254&callback=jsonp255

    https://mm.taobao.com/album/json/get_album_photo_list.htm?user_id=687471686&album_id=10000702574&top_pic_id=0&cover=%2F%2Fimg.alicdn.com%2Fimgextra%2Fi2%2F687471686%2FTB1TlwDLFXXXXbxaXXXXXXXXXXX_!!2-tstar.png&page=3&_ksTS=1465186273660_330&callback=jsonp331

    每个URL包含的参数都很多,有两个参数(_ksTS和callback)看不出规律,于是我打开了另一个相册,发现每个相册的这两个参数都是固定的,第一页的都是155-156,第二页的都是254-255,第三页的都是330-331,且从第二页开始,下一页的callback减去上一页的callback都等于76。

    然而知道这些规律后我发现并没什么卵用,因为我发现即使没有这些参数也能获取数据:

    https://mm.taobao.com/album/json/get_album_photo_list.htm?user_id=687471686&album_id=10000702574&page=1

    https://mm.taobao.com/album/json/get_album_photo_list.htm?user_id=687471686&album_id=10000702574&page=2

    https://mm.taobao.com/album/json/get_album_photo_list.htm?user_id=687471686&album_id=10000702574&page=3

    其实只需用户ID、相册ID和页数即可。

    接下来就是获取每张照片的URL和保存照片了。

    代码

    # -*- coding: utf-8 -*-
    import sys
    reload(sys)
    sys.setdefaultencoding('utf-8')
    '''
    作者:昨夜星辰
    注意事项:该脚本只能在Linux环境用Python 2.x运行,如需在其他环境运行请读者自行修改。
    '''
    import re
    import os
    import shutil
    import requests
    from bs4 import BeautifulSoup
    
    # 创建文件夹,若已存在则删除,再创建。
    def create_folder(path):
        if os.path.exists(path):
            if os.path.isdir(path):
                shutil.rmtree(path)
            else:
                os.remove(path)
        os.mkdir(path)    
        
    # 询问用户
    def ask_user(string):
        while True:
            answer = raw_input(string)
            if answer == 'Y':
                return True
            elif answer == 'N':
                return False
            else:
                print '输入有误,请重新输入!'
    
    # 创建根目录            
    root_folder = '淘女郎'
    create_folder(root_folder)
    
    # 遍历每位淘女郎
    url = 'https://mm.taobao.com/json/request_top_list.htm?page=1'
    bs1 = BeautifulSoup(requests.get(url).text, 'lxml')
    for top in bs1('p', 'top'):
        name = top.find('a').text
        age = top.find('em').text
        city = top.find('span').text
        user_id = top.find('span', 'friend-follow J_FriendFollow')['data-userid']
        print '发现一位美眉,她叫做%s,今年%s,住在%s,现在开始爬取她的个人页面……' % (name, age, city)
        
        # 以淘女郎的昵称新建文件夹
        mm_folder = '%s/%s' % (root_folder, name)
        create_folder(mm_folder)    
        
        # 获取淘女郎的个人资料并保存到文件
        url = 'https://mm.taobao.com/self/info/model_info_show.htm?user_id=' + user_id
        bs2 = BeautifulSoup(requests.get(url).text, 'lxml')
        base_info = bs2.find('ul', 'mm-p-info-cell clearfix')
        info_list = base_info('span')
        result = []
        result.append('昵称:' + info_list[0].text)
        result.append('生日:' + info_list[1].text.strip())
        result.append('所在城市:' + info_list[2].text)
        result.append('职业:' + info_list[3].text)
        result.append('血型:' + info_list[4].text)
        result.append('学校/专业:' + info_list[5].text)
        result.append('风格:' + info_list[6].text)
        result.append('身高:' + base_info.find('li', 'mm-p-small-cell mm-p-height').find('p').text)
        result.append('体重:' + base_info.find('li', 'mm-p-small-cell mm-p-weight').find('p').text)
        result.append('三围:' + base_info.find('li', 'mm-p-small-cell mm-p-size').find('p').text)
        result.append('罩杯:' + base_info.find('li', 'mm-p-small-cell mm-p-bar').find('p').text)
        result.append('鞋码:' + base_info.find('li', 'mm-p-small-cell mm-p-shose').find('p').text)
        print '资料收集完毕,正在保存她的个人资料……',
        filename = '%s/%s.txt' % (mm_folder, name)
        with open(filename, 'w') as f:
            f.write('
    '.join(result))
        print '保存完毕!'
        
        # 获取相册的总页数
        url = 'https://mm.taobao.com/self/album/open_album_list.htm?_charset=utf-8&user_id%20=' + user_id
        bs3 = BeautifulSoup(requests.get(url).text, 'lxml')
        album_total_page = int(bs3.find('input', id='J_Totalpage')['value'])
        
        # 遍历每一页
        for album_page in range(1, album_total_page + 1):
            url = 'https://mm.taobao.com/self/album/open_album_list.htm?_charset=utf-8&user_id%%20=%s&page=%d' % (user_id, album_page)
            bs3 = BeautifulSoup(requests.get(url).text, 'lxml')
            album_count = 1
            
            # 遍历每一个相册
            for album_area in bs3('div', 'mm-photo-cell-middle'):
                # 获取相册的链接、id、名称和照片数
                album_url = 'https:' + album_area.find('h4').find('a')['href']
                album_id = re.search(r'album_id=(d+)', album_url).group(1)
                album_name = album_area.find('h4').find('a').text.strip()
                pic_num = album_area.find('span', 'mm-pic-number').text
                pic_num = re.search(r'd+', pic_num).group(0)
                print '现在开始爬取她的第%d个相册,相册名为:《%s》(%s张)……' % (album_count, album_name, pic_num)
                
                # 根据照片数计算总页数
                total_page = int(pic_num) / 16 + 1            
                
                # 以相册名新建文件夹
                album_folder = '%s/%s' % (mm_folder, album_name)
                create_folder(album_folder)
                
                pic_count = 1
                # 遍历每一页
                for page in range(1, total_page + 1):
                    url = 'https://mm.taobao.com/album/json/get_album_photo_list.htm?user_id=%s&album_id=%s&page=%s' % (user_id, album_id, page)
                    json = requests.get(url).json()
                    for pic in json['picList']:
                        print '现在开始下载该相册的第%d张照片……' % pic_count,
                        pic_url = 'https:' + pic['picUrl']
                        pic_url = re.sub(r'290', '620', pic_url)
                        filename = '%s/%s.jpg' % (album_folder, pic_count)
                        with open(filename, 'wb') as f:
                            f.write(requests.get(pic_url).content)
                        print '下载完毕!'
                        pic_count += 1
                if ask_user('该相册已经下载完毕,是否下载下一个相册?(Y/N)') == False:
                    break
                album_count += 1
            if ask_user('当前页的相册已经下载完毕,是否下载下一页?(Y/N)') == False:
                break
  • 相关阅读:
    Kotlin调用lambda表达式时,lambda中的参数名字省略问题
    Kotlin读取控制台输入
    安卓P(9)及以上Cleartext HTTP traffic to xxx not permitted错误,无法HTTP明文连接错误解决方法
    AS4.0以上查看R.id
    Kotlin Standard.kt解析(also,apply,let.run...)
    ext4文件解包打包
    Kotlin中的var、val和const
    Kotlin的构造方法探究
    Markdown语法
    uniapp遇到的小问题
  • 原文地址:https://www.cnblogs.com/yestreenstars/p/5563382.html
Copyright © 2011-2022 走看看