• requests+xpath之贴吧图片爬取


    这篇博客介绍怎么爬取百度贴吧的图片。使用的是爬虫技术最基础的requests请求加xpath定位提取。

    写这个爬虫是因为有很多贴吧有很多图片,比如表情包吧啊,我很像得到里面的图片,但是如果一张张保存难免太慢了。

    我是为了爬取任意一个贴吧的图片,所以第一个进入的页面肯定是百度贴吧的主页面。

    https://tieba.baidu.com/

    然后我随便输入一个贴吧名,我用表情包吧举例(谁叫他图片多呢)。

    然后进入到表情包吧后,他的URL为:https://tieba.baidu.com/f?ie=utf-8&kw=%E8%A1%A8%E6%83%85%E5%90%A7&fr=search

    我返回主页在随便进入一个贴吧,这次我进Python爬虫吧。

    他的URL为:https://tieba.baidu.com/f?ie=utf-8&kw=Python%E7%88%AC%E8%99%AB&fr=search

    我发现这两个URL只是他的kw属性不同,而且网页会吧我输入的中文自动转码。然后我尝试能不能缩短URL,然后发现能把后面的&fr=search去掉,一样可以请求。所以我就能得到请求贴吧信息的URL:https://tieba.baidu.com/f?ie=utf-8&kw=(你要爬取的吧名,不要带‘吧“吧”字)。

    然后我分析具体贴吧页面,我需要进入每个贴子里面,且我只需要图片,所以我只需得到每个贴子的链接就行了。

    F12进入开发者模式,任意定位到一个贴子,我能找到:

    <a rel="noreferrer" href="/p/5788129292" title="【表情吧官方群】欢迎大家加入!" target="_blank" class="j_th_tit ">【表情吧官方群】欢迎大家加入!</a>

    然后进入这个贴子产看他的URL:https://tieba.baidu.com/p/5788129292

    发现他后半块内容在上面的@href属性,所以我提取到href元素,然后前面加上“https://tieba.baidu.com”就能进入到每个贴子了。下面我测试下:

    import requests  
    from lxml import etree  #要用xpath需要导入的
    from my_fake_useragent import UserAgent    #随机请求头的一个包
    def
    get_one_page(url): headers = { 'User-Agent': UserAgent().random(), 'Referer': 'https: // tieba.baidu.com / index.html', } base = 'https://tieba.baidu.com' response = requests.get(url, headers).text html = etree.HTML(response) link_list = html.xpath('//ul[@id="thread_list"]//a[@rel="noreferrer"and@class="j_th_tit "]/@href') link_list = map(lambda link: base + link, link_list) for link in link_list: print(link) def main(): url = 'https://tieba.baidu.com/f?ie=utf-8&kw=%E8%A1%A8%E6%83%85%E5%90%A7' get_one_page(url) if __name__ == '__main__': main()

    输出结果部分:

    C:UsersUserAppDataLocalProgramsPythonPython37python.exe G:/Python/code/requeats/try.py
    https://tieba.baidu.com/p/5788129292
    https://tieba.baidu.com/p/4789404681
    https://tieba.baidu.com/p/6478509408
    https://tieba.baidu.com/p/6497831229
    https://tieba.baidu.com/p/6497828481
    ……

    这里我要推荐一个包,就是上面的my_fake_useragent 包,这个包是随机返回一个User-Agent,这样我就不用辛苦构建一个User-Agent池了。如果你要安装只需pip install fake_useragent 就行了,为啥我这是my_fake_useragent,是因为我在pycharm一开始下载的就是这个,所以大家要使用fake_useragent就行了。

    以及上面我headers里面的referer测试的时候是不加也能请求的,但是如果你大量爬取,难免要检查,所以headers写的也多当然越好。

    回到原题,我已经提取到每个贴子的URL了,我需要进入每个贴子爬取图片。

    因为我要找到图片所在块的规律,所以我得进入一个至少有两三张图片且有广告的贴子,且我不要广告的图片。

    然后我进入:https://tieba.baidu.com/p/6299996178

    我会发现这个贴子有非常多的图,我要找到不同回复的图片,以确保我找的是一个大众的规律。

    第一张图

    <cc>            <div class="j_ueg_post_content p_forbidden_tip" style="display:none;">该楼层疑似违规已被系统折叠&nbsp;<a rel="noopener" href="###" class="p_forbidden_post_content_unfold" style="display:;">隐藏此楼</a><a rel="noopener" href="###" class="p_forbidden_post_content_fold" style="display:none;">查看此楼</a></div><div id="post_content_127945257402" class="d_post_content j_d_post_content " style="display:;">            长更,图源各处,自己搜集<br><img class="BDE_Image" src="https://imgsa.baidu.com/forum/w%3D580/sign=7015ef856381800a6ee58906813533d6/8a274b90f603738d083a1a28bc1bb051f819ec6a.jpg" size="52322" changedsize="true" width="560" height="560"><br><img class="BDE_Image" src="https://imgsa.baidu.com/forum/w%3D580/sign=cf7758d65be736d158138c00ab514ffc/b2ed38dbb6fd5266a76f2a1ea418972bd407361d.jpg" size="44412" changedsize="true" width="560" height="560"><br><img class="BDE_Image" src="https://imgsa.baidu.com/forum/w%3D580/sign=4d6f86f8721ed21b79c92eed9d6fddae/500163d0f703918f6a172ac05e3d269758eec498.jpg" size="11218" width="240" height="240"><br><img class="BDE_Image" src="https://imgsa.baidu.com/forum/w%3D580/sign=c3ee0de7accc7cd9fa2d34d109012104/97e47c1ed21b0ef474948e54d2c451da81cb3e65.jpg" size="28050" width="482" height="360"><br><img class="BDE_Image" src="https://imgsa.baidu.com/forum/w%3D580/sign=7d835c046809c93d07f20effaf3cf8bb/14599f2f07082838aba4ca1eb799a9014c08f131.jpg" size="79960" changedsize="true" width="560" height="479"></div><br>                        </cc>

    第二张图,来自另一个回复:

    <cc>            <div class="j_ueg_post_content p_forbidden_tip" style="display:none;">该楼层疑似违规已被系统折叠&nbsp;<a rel="noopener" href="###" class="p_forbidden_post_content_unfold" style="display:;">隐藏此楼</a><a rel="noopener" href="###" class="p_forbidden_post_content_fold" style="display:none;">查看此楼</a></div><div id="post_content_127945275558" class="d_post_content j_d_post_content " style="display:;">            <img class="BDE_Image" src="https://imgsa.baidu.com/forum/w%3D580/sign=731151b14810b912bfc1f6f6f3fdfcb5/2eb30b7b02087bf4b4d141e3fdd3572c11dfcfa5.jpg" size="73764" changedsize="true" width="560" height="560"></div><br>                        </cc>

    广告:

    <div class="middle_image_content">
                        <a class="j_click_stats img_wrap" target="_blank" href="https://www.baidu.com/baidu.php?url=a00000jCx3p_KaPSJbxITB8iy2YQnH0i7MPwHjhCd4ZW9ROunXB4q7Su9mrP0PSLdibjxpor5i3bIc2IP5qPh0nU2MvXtTJEod0U44YTogcAYwcHPHbNGdATM7kr_jUPIVgfYjcKf7HG5NcuGhv1CkXAXAGu4U10zIYrxGx9O6kXpFI9h4or8i76wHQqj_kpvXFN1sVGIjqq7JXKDF7yYiv64_fk.7D_NR2Ar5Od66ux4g_3_ac2ampCPUn2XqauhZVm9kOSoQqpRLxOOqh5gOl33xOOS1ZWOqx1xjMhH7wX17ZRSqlTOsOhSZOOO_k35OyunN7enRIHeLG1lZmrW017MlZ4Eu6zxOOqbQOOgOOO_eXjOdo__o_vyUdBmSoG_A-muCyn--xuER0.U1Yk0ZDqdVj01tHJEUEHYfKY5ULnYOL73PAd0A-V5HRkn0KM5fKdpHY0TA-b5Hc30APGujYznWb0UgfqnHmdnHR0mhnqnfKopHYs0ANGujYdnj03nWbv0AFG5fKVm1Y0TgKGujYs0Z7Wpyfqn0KzuLw9u1Ys0AqvUjYznWf3PzYkPdt1nj0snjDVn-t1n1f4nadbX-t1PHTdPadbX-t1rjRkPzYkg1fknj6LQywlg1fznWnzQH7xPjR1nHRVnNtYPHbvPBYs0A7B5HKxn0K-ThTqn0KsTjYs0A4vTjYsQW0snj0snj0s0AdYTjY0uAwETjY0ThCqn0K1XWY0IZN15HTvPHbknjnYnH0dPHc3Pjc1rHR0ThNkIjYkPH6krHb1PjbkrHTd0ZPGujYsPAcYuHD1uyDzPvc3nW010ZK85H00ULnqP0KVIZ-suHY10A7bIZ-suHYkrjT0mgwspyfqn0KWTA-b5HDsnjD0TAkGujYsnj0z0APCpyfq0A7sTZu85fKYmg6q0AdYT1YkPWRkPfKEmLKW5HDWnansc10Wnansc1cYPinWc1D8nj0sc1D8nj0scznWnansc1D8nj0Wcznsc10Wnansc10Wnansc1ndnansc10WnankQW0sc1D8nj0sc1D8nj0sc108nj0sc108nj0sc108nj0sc1D8nj0Wnansc10WnankQW0snansc10WnanWnansc100TZfqn1ndn1b0TAq1pgwGUv3qn0KsULPGIA-EU-qWpA78uvNxThN9Tvq85H00ULFGgvdY5HDvPHDd0AdYgLKY5H00myPWIjY0uAPWujY0uAPzTjY0uANvIZ0q0ZP85fKsThqb5fKEmLKWgvk9m-q1IA7YIgnqn0KEmLKWgvP_pyPo5H0Wnansc10Wnansc1D8nj0sc1D8nj0s0AuYXgK-5H00myw1U-q15H00myw1U-qGT1Y0mhd_5H00Uy7WPHY0UvDq0A7bmv_qn0K_IjYs0ZPW5H00Ih-buyqGujY0mhN1UjYs0A-1uAsqn0KEUAw_5H00TZFEuv7bIWYs0A71XHYs0A7bTZTqnHDYn0K9uZKC5HmYn0KGTL0quLGCXZb0pZP_u1Ys0jDqn00z5f015f0Y5f0d5H00PWYs0jTqn0035H00rHY0TZFEudqYT1YkP1m1rHbYPHR4n1DYrjR3nWnzn0KsThqMgLK15HbznjRdnHTYPHbYn1c1PHc4rjT0TZFEudqYpHYs0ZKzUvIxTAbq0ZKWpHYs0ZPJpjYzPHR0mMNYmyTqn0K8ugfqn0KWThnqnWD4Pjb" data-locate="pb_图片">
                            <img class="BDE_Image" src="https://aod-image-material.cdn.bcebos.com/5/pic/e49b173f4f765829e251b905696dc386.jpg" ad-dom-img="true">
                            
                        </a>
                        <div class="ad_bottom_view">
                            <span class="now_date">2020-02-18 10:38</span>
                            <span class="label_text"> 广告</span>
                        </div>
                    </div>

    这里从我截取的HTML代码就能知道,我需要的图片,直接在CC标签下属性@class="BDE_Image的img标签里的@src属性里。

    然后我试着爬取一页贴子里的第一页回复里的图片(有点绕哈哈)。

    import requests
    from lxml import etree
    from my_fake_useragent import UserAgent
    
    
    def get_one_page(url):
        headers = {
            'User-Agent': UserAgent().random(),
            'Referer': 'https: // tieba.baidu.com / index.html',
        }
        base = 'https://tieba.baidu.com'
        response = requests.get(url, headers).text
        html = etree.HTML(response)
        link_list = html.xpath('//ul[@id="thread_list"]//a[@rel="noreferrer"and@class="j_th_tit "]/@href')
        link_list = map(lambda link: base + link, link_list)
        return link_list
    
    
    def parse_one_page(link):
        headers = {
            'User-Agent': UserAgent().random(),
            'Cookie': '(填你自己的)',
        }
        imgs = []
        url = link
        response = requests.get(url, headers).text
        html = etree.HTML(response)
        img = html.xpath('//cc//img[@class="BDE_Image"]//@src')
        imgs.append(img)
        return imgs
    
    
    def main():
        url = 'https://tieba.baidu.com/f?ie=utf-8&kw=%E8%A1%A8%E6%83%85%E5%90%A7'
        for page in get_one_page(url):
            for imgs in parse_one_page(page):
                for img in imgs:
                    print(img)
    
    
    if __name__ == '__main__':
        main()

    输出结果部分:

    C:UsersUserAppDataLocalProgramsPythonPython37python.exe G:/Python/code/requeats/try.py
    https://imgsa.baidu.com/forum/w%3D580/sign=9fa533f53cadcbef01347e0e9caf2e0e/f0dbfd039245d6889b38b895a9c27d1ed21b24ea.jpg
    https://imgsa.baidu.com/forum/w%3D580/sign=cdf24ef44ba98226b8c12b2fba83b97a/facc14ce36d3d5399036c3423287e950342ab0ca.jpg
    http://tiebapic.baidu.com/forum/w%3D580/sign=d106bd7a4e82b2b7a79f39cc01accb0a/7436b812c8fcc3ceb3bfec4d8545d688d43f2072.jpg
    http://tiebapic.baidu.com/forum/w%3D580/sign=7cbc896bab315c6043956be7bdb0cbe6/c8cbaa64034f78f06b74bf8d6e310a55b3191c72.jpg
    http://tiebapic.baidu.com/forum/w%3D580/sign=6c8ab7c8de177f3e1034fc0540ce3bb9/92ad86d6277f9e2fba4a38760830e924b899f373.jpg
    http://tiebapic.baidu.com/forum/w%3D580/sign=c8b5121def1986184147ef8c7aec2e69/81379213b07eca801e96ae9b862397dda04483e5.jpg
    http://tiebapic.baidu.com/forum/w%3D580/sign=fc409f2142fbb2fb342b581a7f4b2043/ae30fcfaaf51f3de11212e1883eef01f3b2979e5.jpg
    http://tiebapic.baidu.com/forum/w%3D580/sign=6b8fd58ab0efce1bea2bc8c29f50f3e8/ad770eb30f2442a722536bd9c643ad4bd1130217.jpg
    http://tiebapic.baidu.com/forum/w%3D580/sign=0e1dfe512d292df597c3ac1d8c305ce2/b1245bafa40f4bfb611ea52a144f78f0f63618a3.jpg
    http://tiebapic.baidu.com/forum/w%3D580/sign=43e7890afcf81a4c2632ecc1e72b6029/a17ed009b3de9c829e8cc2f17b81800a18d8438c.jpg
    http://tiebapic.baidu.com/forum/w%3D580/sign=7b203fe3dcef76093c0b99971edca301/cf259345d688d43ff040aa8c6a1ed21b0ff43b8c.jpg
    ……

    这样我就提取到了每个图片的网址了。

    然后我要添加多的操作方便人为的控制:

    ①爬取什么贴吧,是自己输入贴吧名,当然如果你只是为了爬取某个贴吧可以不要这个。

    ②要爬取几页贴子,因为有些贴吧图片众多,如果你只是为了用来斗斗图,就不用爬取很多,所以我可以设置爬取多少页,当然也设置爬取多少张图片就停止程序也很不错。

    ③爬取每个贴子里面的每页回复。因为贴子质量不同,有些贴子无人问津,有的贴子却有数不胜数的图片,所以我也不可能找到一个适当的值来约束所有的贴子,所以这里我是提取每个贴子里面的总页数,然后用总页数进行循环来爬取每一页回复。

    ④我要提取图片网址后面他的名字来保存,这里我用os包下的os.path.split。比如:http://tiebapic.baidu.com/forum/w%3D580/sign=7b203fe3dcef76093c0b99971edca301/cf259345d688d43ff040aa8c6a1ed21b0ff43b8c.jpg这个图片,我保存时他的名字是cf259345d688d43ff040aa8c6a1ed21b0ff43b8c.jpg,这样的的好处,是不用干巴巴的用1,2,3,4这样递归增加来当图片名,而且图片后缀名也有不同,我自己手动写很容易出错,比如gif图片,你加jpg他就不动了,这样的图片就毫无意义了。所以我使用os.path.split就能直接提取到图片后半部分,方便了不少。

    ⑤保存图片,我使用的是urllib下的urlretrieve,导入时from urllib.request import urlretrieve。

    全部代码:

    import requests
    from lxml import etree
    from urllib.request import urlretrieve
    from my_fake_useragent import UserAgent
    import os
    
    
    def get_one_page(url):
        headers = {
            'User-Agent': UserAgent().random(),
            'Referer': 'https: // tieba.baidu.com / index.html',
        }
        base = 'https://tieba.baidu.com'
        response = requests.get(url, headers).text
        html = etree.HTML(response)
        link_list = html.xpath('//ul[@id="thread_list"]//a[@rel="noreferrer"and@class="j_th_tit "]/@href')
        link_list = map(lambda link: base + link, link_list)
        return link_list
        # for link in link_list:
        #     print(link)
    
    
    def parse_one_page(link):
        headers = {
            'User-Agent': UserAgent().random(),
            'Cookie': '(填你自己的)',
        }
        imgs = []
        url = link + "?pn=1"
        response = requests.get(url, headers).text
        html = etree.HTML(response)
        maxnumber = html.xpath('//li[@class="l_reply_num"]/span[2]/text()')[0]
        for i in range(1, int(maxnumber) + 1):
            try:
                url = link + "?pn={}".format(i)
                response = requests.get(url, headers).text
                html = etree.HTML(response)
                img = html.xpath('//cc//img[@class="BDE_Image"]//@src')
                imgs.append(img)
            except:
                break
        return imgs
    
    
    def main():
        print('该爬虫功能为百度贴吧图片爬取!!!')
        kw = input('请输入所需爬取贴吧名:')
        number = input('请输入所需爬页数:')
        j = 1
        for i in range(int(number)):
            url = 'https://tieba.baidu.com/f?kw=' + kw + '&ie=utf-8&pn={}'.format(i * 50)
            try:
                for page in get_one_page(url):
                    print(page)
                    for imgs in parse_one_page(page):
                        for img in imgs:
                            print('正在保存第' + str(j) + '张图片。')
                            suffix = os.path.split(img)[1]
                            urlretrieve(img, 'C:\Users\User\Desktop\图片\' + kw + '\' + str(suffix))
                            j += 1
            except:
                break
        print('保存完毕!')
    
    
    if __name__ == '__main__':
        main()

    这里我使用了自己的cookie,大家要用的话请添加你自己的。我保存的使桌面上的一个文件夹,保存时会输出保存了多少页。当然因为我是自己使用图片,并不是工作需要,所以没用多线程来写,大家有需要可以自己添加多线程。

    输出结果部分:

    C:UsersUserAppDataLocalProgramsPythonPython37python.exe G:/Python/code/requeats/tieba.py
    该爬虫功能为百度贴吧图片爬取!!!
    请输入所需爬取贴吧名:表情包
    请输入所需爬页数:1
    https://tieba.baidu.com/p/6416580758
    正在保存第1张图片。
    正在保存第2张图片。
    正在保存第3张图片。
    正在保存第4张图片。
    正在保存第5张图片。
    正在保存第6张图片。
    正在保存第7张图片。
    正在保存第8张图片。
    正在保存第9张图片。
    正在保存第10张图片。
    正在保存第11张图片。
    ……
    ……
    ……

    下面是爬取结果,一页贴子居然爬了两千多张,可见表情包吧的产量。

  • 相关阅读:
    vagrant使用
    商品分类
    猜你喜欢
    [NOIP2012]开车旅行
    bzoj 1029: [JSOI2007]建筑抢修
    bzoj 2127: happiness
    bzoj 2561: 最小生成树
    bzoj 3331: [BeiJing2013]压力
    数组中简便方法求最大值,最小值,平均值,求和,和个数
    #include 和 #import 的区别, @class 的含义
  • 原文地址:https://www.cnblogs.com/zj666666/p/12325341.html
Copyright © 2020-2023  润新知