V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
huan1043269994
V2EX  ›  Python

爬虫养成记--顺藤摸瓜回首掏(女生定制篇)

  •  
  •   huan1043269994 · 2020-04-28 10:28:11 +08:00 · 2855 次点击
    这是一个创建于 1668 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我们研发开源了一款基于 Git 进行技术实战教程写作的工具,我们图雀社区的所有教程都是用这款工具写作而成,欢迎 Star

    如果你想快速了解如何使用,欢迎阅读我们的 教程文档哦

    本文由图雀社区成员 灿若星空 写作而成,欢迎加入图雀社区,一起创作精彩的免费技术教程,予力编程行业发展。

    前情回顾

    在上篇教程爬虫养成记——先跨进这个精彩的世界(女生定制篇)中我们已经可以将所有小哥哥的封面照片抓取下来,但仅仅是封面图片在质量和数量上怎么能满足小仙女们的要求呢?在本篇教程中,我们串起一根姻缘“线”,来把这一系列的小哥哥们都收入囊中。

    出门先化妆

    小仙女们出门约会总会“淡妆浓抹总相宜”,那爬虫出门去爬取数据,也得打扮打扮啊,不然怎么能让男神们都乖乖地跟着走呢?

    爬虫的“化妆”可不是“妆前乳 --> 粉底 --> 遮瑕 --> 散粉 --> 画眉 --> 口红”等这些步骤,其目的是为了让对方网站更加确信来访者不是爬虫程序,而是一个活生生的人。人们通过操控浏览器来访问网站,那么爬虫程序只需要模仿浏览器就可以了。 那就来看看浏览器在打开网页时都画了那些“妆”。

    8GMVwd.png

    打开 Chrome 并打开调试台,切换到 NetWork 选项卡,此时访问 https://www.nanrentu.cc/sgtp/ , 这是时候会看到调试台里出现了很多链接信息,这么多链接到底哪个是我们所需要的呢?回想一下上一篇内容,首先是要获得 HTML 文档,再从此文档中提取出图片的链接,所以目标有了,就是找到浏览器获取到这个 HTML 文档的那个链接。

    Chrome 知道这么多链接信息肯定会让开发者陷入茫然,所以给链接进行了归类,点击上方 Doc 分类,再点击那唯一的一条链接,就会看到获取此 HTML 文档链接的详细信息了。此时我们关注主要 Request Headers 这个里面的内容。浏览器通过 http 协议与服务器交互获取信息,爬虫是通过模仿浏览器发出 http 协议获取信息,其中最重要的一个模仿点就是 Request Headers 。

    http 协议里面的“瓶瓶罐罐”

    让男生看女孩子化妆用的那些瓶瓶罐罐估计会陷入沉思,这是 BB 霜,那是粉底液,还有散粉、眼影、遮瑕膏,更不用说各种色号的口红啦。那女孩子看到这 http 里面的各项内容时估计也会一脸懵逼,其这比化妆品简单多了,我们只需简单了解,就能给爬虫画出精致妆容。

    :authority: www.nanrentu.cc
    :method: GET   // 自定义请求头 请求方法
    :path: /sgtp/  // 自定义请求头 请求路径
    :scheme: https // 自定义请求头 请求方式
    // 所接受的内容格式
    accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
    // 所接受的编码方式
    accept-encoding: gzip, deflate, br
    // 所接受的语言
    accept-language: zh-CN,zh;q=0.9
    // 缓存控制:告诉服务器客户端希望得到一个最新的资源
    cache-control: max-age=0
    cookie: UM_distinctid=170a5a00fa25bf-075185606c88b7-396d7407-100200-170a5a00fa3507; Hm_lvt_45e50d2aec057f43a3112beaf7f00179=1583326696,1583756661; CNZZDATA1274895726=1196969733-1583323670-%7C1583752625; Hm_lpvt_45e50d2aec057f43a3112beaf7f00179=1583756721
    sec-fetch-dest: document
    sec-fetch-mode: navigate
    sec-fetch-site: none
    sec-fetch-user: ?1
    // 屏蔽 HTTPS 页面出现 HTTP 请求警报
    upgrade-insecure-requests: 1
    user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36
    

    这么多的信息不用都给爬虫加上,因为这网站的防爬措施等级不高,暂时只要关键的两个就可以了。

    • cookie: 这是存储在浏览器里面一段文本,有时包含了验证信息和一些特殊的请求信息
    • user-agent:用于标识此请求是由什么工具所发出的 关于 User-Agent 的详细信息可以参考此篇博文 谈谈 UserAgent 字符串的规律和伪造方法

    但是当爬取其他网站时可能会有所需要,在这里赘述这么多原因就是希望大家能明白伪装爬虫的重要性,以及怎么获取这些伪装信息。

    // 建立一个名叫 headers 的字典
    headers = {
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36',
        'cookie': 'UM_distinctid=170a5a00fa25bf-075185606c88b7-396d7407-100200-170a5a00fa3507; CNZZDATA1274895726=1196969733-1583323670-%7C1583925652; Hm_lvt_45e50d2aec057f43a3112beaf7f00179=1583326696,1583756661,1583926583; Hm_lpvt_45e50d2aec057f43a3112beaf7f00179=1583926583'
    }
    // 发送请求时带上请求头
    response = requests.get(baseUrl,headers=headers)
    

    顺藤摸瓜

    一个网站是由若干个网页组合而成的,网页中充满着各种超链接,从这网页链接到那个网页,如果我们想要更多小哥哥,那就得首先分析出串联起他们那些超链接,然后就可以顺藤摸瓜咯。

    超连接元素.png

    当把鼠标发放到标题上时,标题的颜色发生了变化,证明这一元素为超连接,点击标题浏览器会自动打开一个 tab 标签页,来显示网页,注意到下方的页码标签,是这些元素串联起了整个图集。

    8EBD9U.png

    点击“末页”观察 url 发生了什么变化

    末页的 url: https://www.nanrentu.cc/sgtp/36805_7.html

    首页的 url: https://www.nanrentu.cc/sgtp/36805.html

    看起来有点意思了,末页的 url 比首页的 url 多了“_7”,接下来再点击分别进入第 2 页,第 3 页……观察 url 的变化,可得出下表。分别是 页面、url 形式:

    | 首页 | https://www.nanrentu.cc/sgtp/36805.html |
    | 第 2 页 | https://www.nanrentu.cc/sgtp/36805_2.html |
    | 第 3 页 | https://www.nanrentu.cc/sgtp/36805_3.html |
    | 第 4 页 | https://www.nanrentu.cc/sgtp/36805_4.html |
    | 第 5 页 | https://www.nanrentu.cc/sgtp/36805_5.html |
    | 第 6 页 | https://www.nanrentu.cc/sgtp/36805_6.html |
    | 第 7 页 | https://www.nanrentu.cc/sgtp/36805_7.html |

    多点几个组图,也会发现同样规律。这样就明了很多了,我们已经分析清楚了这个跟“藤”的开头与结尾,接下来就可以敲出代码让爬虫开始“摸瓜”咯。

    摸瓜第 1 步:提取标题链接

    这个操作与上篇博文中所介绍的一样,打开调试台切换到 Elements 选项卡就能开始探索提取了。

    8Ech4I.png

    摸瓜第 2 步:提取末页链接,得出组图页数

    8ERtu8.png

    通过观察 HTML 元素结构,可发现包含末页的 <li> 标签为其父元素<ul>的倒数第二个子元素,所以可得出以下的 css 选择器

    .page > ul > li:nth-last-child(2) > a

    摸瓜第 3 步:根据首尾链接构造 url

    为了构造 url 更加方便,我们可以把首页 https://www.nanrentu.cc/sgtp/36805.html 变为 https://www.nanrentu.cc/sgtp/36805_1.html, 在浏览器中打开带有后缀的这个网址,依然能够成功访问到首页,不要问我为什么?这可能就是程序员之间的一种默契吧~

    摸瓜第 4 步:存储图片,摸瓜成功

    完整的代码如下:

    import requests
    from pyquery import PyQuery as pq
    import uuid
    
    headers = {
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36',
        'cookie': 'UM_distinctid=170a5a00fa25bf-075185606c88b7-396d7407-100200-170a5a00fa3507; CNZZDATA1274895726=1196969733-1583323670-%7C1583925652; Hm_lvt_45e50d2aec057f43a3112beaf7f00179=1583326696,1583756661,1583926583; Hm_lpvt_45e50d2aec057f43a3112beaf7f00179=1583926583'
    }
    def saveImage(imgUrl,name):
        imgResponse = requests.get(imgUrl)
        fileName = "学习文件 /%s.jpg" % name
        if imgResponse.status_code == 200:
            with open(fileName, 'wb') as f:
                f.write(imgResponse.content)
                f.close()
    
    def getPic(urlArray):
        for url in urlArray:
            res = requests.get(url,headers=headers)
            if res.status_code == 200:
                doc = pq(res.text)
                imgSrc = doc('.info-pic-list > a > img').attr('src')
                print(imgSrc)
                saveImage(imgSrc,uuid.uuid1().hex)
        
    
    def createUrl(indexUrl,allPage):
        baseUrl = indexUrl.split('.html')[0]
        urlArray = []
        for i in range(1,allPage):
            tempUrl = baseUrl+"_"+str(i)+".html"
            urlArray.append(tempUrl)
        return urlArray
    
    def getBoys(link):
        # 摸瓜第 1 步:获取首页连接
        picIndex = link.attr('href')
        #  摸瓜第 2 步:打开首页,提取末页链接,得出组图页数
        res = requests.get(picIndex,headers=headers)
        print("当前正在抓取的 picIndex: " + picIndex)
        if res.status_code == 200:
            with open("picIndex.html",'w',encoding="utf-8") as f:
                f.write(res.text)
            doc = pq(res.text)
            lastLink = doc('.page > ul > li:nth-last-child(2) > a').attr('href')
            # 字符串分割,得出全部的页数
            if(lastLink is None):
                return
            # 以.html 为分割符进行分割,取结果数组中的第一项
            temp = lastLink.split('.html')[0]
            # 再以下划线 _ 分割,取结果数组中的第二项,再转为数值型
            allPage = int(temp.split('_')[1])
            # 摸瓜第 3 步:根据首尾链接构造 url
            urlArray = createUrl(picIndex,allPage)
            # 摸瓜第 4 步:存储图片,摸瓜成功
            getPic(urlArray)
    
    def main():
        baseUrl = "https://www.nanrentu.cc/sgtp/"
        response = requests.get(baseUrl,headers=headers)
        if response.status_code == 200:
            with open("index.html",'w',encoding="utf-8") as f:
                f.write(response.text)
            doc = pq(response.text)
            # 得到所有图集的标题连接
            titleLinks = doc('.h-piclist > li > a').items()
            # 遍历这些连接
            for link in titleLinks:
                getBoys(link)
    
    if __name__ == "__main__":
        main()
    

    运行结果:

    8egHjx.png

    回首掏

    回顾整个爬虫程序,它是连续式流水线作业,每一步之间都是环环相扣,所以在写程序前自己一定要把整个流水线的每个环节都考虑清楚,把它们之间的顺序依赖关系化成一个简易的流程图,对着流程图再写程序就会清晰很多。我们可以把每一个模块都写成一个函数,先对函数做好单元测试,再把这些函数按顺序组合起来就行啦。分而治之,有机组合这就是编程的奥义。再复杂的项目,都是由一个个模块组建起来的,这和搭积木是一样的道理。

    8eoaDA.png

    这个流程图只用单项箭头画出了获取一张图片的全部过程,这就相当于一个工人在干活,我们的计算机是一个大工厂里面有成千上万个工人,只让一个工人干活其他的人都在为他加油嘛?那也太说不过去,在下一篇文章中,我们将画出完整的流程图,分析出其他工人没活干的原因,然后充分调动起计算机的算力,来提升程序的运行效率。

    如果您觉得我们写得还不错,记得 点赞 + 关注 + 评论 三连🥰🥰🥰,鼓励我们写出更好的教程💪

    想要学习更多精彩的实战技术教程?来图雀社区逛逛吧。如果你觉得我们写得还不错,可以点击这个链接关注我们的微信公众号❤️

    5 条回复    2020-04-28 21:32:24 +08:00
    hbolive
        1
    hbolive  
       2020-04-28 11:00:57 +08:00
    写得不错,昨天好像就发了,但那篇为啥看不到图?今天就可以了。。
    ZoeYn
        2
    ZoeYn  
       2020-04-28 11:17:00 +08:00
    保姆式教程,👍
    huan1043269994
        3
    huan1043269994  
    OP
       2020-04-28 14:44:14 +08:00
    @hbolive 昨天那个估计图床塌了🌚
    CeresLC
        4
    CeresLC  
       2020-04-28 14:48:24 +08:00
    怎么有一种 gay gay 的感觉?
    nofacetou
        5
    nofacetou  
       2020-04-28 21:32:24 +08:00 via Android
    这教程口气,把男的女的都当弱智了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   987 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 20:43 · PVG 04:43 · LAX 12:43 · JFK 15:43
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.