zoukankan      html  css  js  c++  java
  • 【Python网络爬虫四】通过关键字爬取多张百度图片的图片

    最近看了女神的新剧《逃避虽然可耻但有用》,同样男主也是一名程序员,所以很有共鸣

    被大只萝莉萌的一脸一脸的,我们来爬一爬女神的皂片。

    百度搜索结果:新恒结衣

    本文主要分为4个部分:

      1.下载简单页面

      2.爬取多张图片

      3.页面解码

      4.爬取过程排错处理

    1.下载简单页面

    通过查看网页的html源码,分析得出,同一张图片共有4种链接:

    {"thumbURL":"http://img5.imgtn.bdimg.com/it/u=2243348409,3607039200&fm=23&gp=0.jpg",
    "middleURL":"http://img5.imgtn.bdimg.com/it/u=2243348409,3607039200&fm=21&gp=0.jpg",
    "hoverURL":"http://img5.imgtn.bdimg.com/it/u=2243348409,3607039200&fm=23&gp=0.jpg",
    "objURL":"http://attachments.gfan.com/attachments2/day_110111/1101112033d77a4a8eb2b00eb1.jpg"}

    主要区别是分辨率不同,objURL是图片的源也是最清楚的一张。经测试,前三种都有反爬虫措施,用浏览器可以打开,但是刷新一次就403 Forbidden。用爬虫获取不到图片

    第四种objURL是指图片的源网址,获取该网址会出现三种情况:

    1. 正常。继续下载
    2. 403 Forbidden。用continue跳过。
    3. 出现异常。用try except处理。
     1 #coding: utf-8
     2 import os
     3 import re
     4 import urllib
     5 import urllib2
     6 
     7 def getHtml(url):
     8     page=urllib.urlopen(url)
     9     html=page.read()
    10     return html
    11 
    12 def getImg(html):
    13     reg=r'"objURL":"(.*?)"'   #正则
    14     # 括号表示分组,将括号的内容捕获到分组当中
    15     #  这个括号也就可以匹配网页中图片的url了
    16     imgre=re.compile(reg)
    17     print imgre
    18     imglist=re.findall(imgre,html)
    19     l=len(imglist)
    20     print l
    21     return imglist
    22 
    23 def downLoad(urls,path):
    24     index = 1
    25     for url in urls:
    26         print("Downloading:", url)
    27         try:
    28             res = urllib2.Request(url)
    29             if str(res.status_code)[0] == "4":
    30                 print("未下载成功:", url)
    31                 continue
    32         except Exception as e:
    33             print("未下载成功:", url)
    34         filename = os.path.join(path, str(index) + ".jpg")
    35         urllib.urlretrieve(url,filename)        #直接将远程数据下载到本地。
    36         index += 1
    37 html =getHtml("http://image.baidu.com/search/index?tn=baiduimage&ipn=r&ct=201326592&cl=2&lm=-1&st=-1&fm=result&fr=&s"
    38               "f=1&fmq=1484296421424_R&pv=&ic=0&nc=1&z=&se=1&showtab=0&fb=0&width=&height=&face=0&istype=2&ie=utf-8&"
    39               "word=新垣结衣&f=3&oq=xinyuanj&rsp=0")
    40 Savepath="D:TestDownload"
    41 downLoad(getImg(html),Savepath)

    其中urlretrieve方法
    直接将远程数据下载到本地。

            urllib.urlretrieve(url[, filename[, reporthook[, data]]])
            参数说明:
            url:外部或者本地url
            filename:指定了保存到本地的路径(如果未指定该参数,urllib会生成一个临时文件来保存数据);
            reporthook:是一个回调函数,当连接上服务器、以及相应的数据块传输完毕的时候会触发该回调。我们可以利用这个回调函数来显示当前的下载进度。
            data:指post到服务器的数据。该方法返回一个包含两个元素的元组(filename, headers),filename表示保存到本地的路径,header表示服务器的响应头。

    女神的照片就这么被爬下来了

    2.爬取更多图片

    等等,这个方法不过瘾呀,百度图片的页面是动态加载的,需要你往下滑才能继续加载后面的照片,我们这才完成了第一步,只爬到了30张图。

    打开浏览器,按F12,切换到Network标签,然后将网页向下拉。这时浏览器地址栏的网址并没有改变,而网页中的图片却一张张增加,说明网页在后台与服务器交互数据。好的发现了就是这个家伙

    XHR英文全名XmlHttpRequest,中文可以解释为可扩展超文本传输请求。Xml可扩展标记语言,Http超文本传输协议,Request请求。XMLHttpRequest对象可以在不向服务器提交整个页面的情况下,实现局部更新网页。当页面全部加载完毕后,客户端通过该对象向服务器请求数据,服务器端接受数据并处理后,向客户端反馈数据。

    点开对比Request的URL,可以发现,基本上是一样的,除了末尾一点点。

    只是pn的值不一样,测试发现,pn应该是表示当前请求的图片序号,rn表示更新显示图片的数量。

    pn=90&rn=30&gsm=5a&1484307466221=
    pn=120&rn=30&gsm=5a&1484307466223=
    pn=150&rn=30&gsm=78&1484307469213=
    pn=180&rn=30&gsm=78&1484307469214=
    pn=210&rn=30&gsm=b4&1484307553244=

    推荐一款Chrom的插件,Json handle。很方便。

    百度云下载地址。链接:http://pan.baidu.com/s/1qXZEjaW 密码:nxr5

     

    现在知道了,其实我们只要不断访问这个Request URL,改变他的pn值理论上就可以实现下载多张图片了。

    1 def getMoreURL(word):
    2     word = urllib.quote(word)
    3     url = r"http://image.baidu.com/search/acjson?tn=resultjson_com&ipn=rj&ct=201326592&fp=result&queryWord={word}" 
    4           r"&cl=2&lm=-1&ie=utf-8&oe=utf-8&st=-1&ic=0&word={word}&face=0&istype=2nc=1&pn={pn}&rn=60"
    5     urls = (url.format(word=word, pn=x) for x in itertools.count(start=0, step=30))
    6     #itertools.count 0开始,步长30,迭代
    7     return urls

    其中,urllib.quote的作用是,对url就行处理。

    按照标准, URL 只允许一部分 ASCII 字符(数字字母和部分符号),其他的字符(如汉字)是不符合 URL 标准的。
    所以 URL 中使用其他字符就需要进行 URL 编码。

    URL 中传参数的部分(query String),格式是:
    name1=value1&name2=value2&name3=value3 
    
    假如你的 name 或者 value 值中有『&』或者『=』等符号,就当然会有问题。所以URL中的参数字符串也需要把『&=』等符号进行编码。

    URL编码的方式是把需要编码的字符转化为 %xx 的形式。通常 URL 编码是基于 UTF-8 的(当然这和浏览器平台有关)。
    例子:
    比如『我』,unicode 为 0x6211, UTF-8 编码为 0xE6 0x88 0x91,URL 编码就是 
    %E6%88%91

    itertools.count(start,step)的作用是一个迭代器,2个参数是开始和步长,这样就得到一个等差数列,这个数列填入url中,就得到了我们的urls。

    试了下,转到解析得到的数据,objURL居然不是一个http的图片,查资料,显示下一步要做的就是解码

    "objURL":"ippr_z2C$qAzdH3FAzdH3Fta_z&e3Bi1fsk_z&e3Bv54AzdH3FkufAzdH3Fw6vitejAzdH3F99lwbdublldlnvjaavmc8c01ba8a8m1mu0vjwama_z&e3B3r2"

    3.页面解码

    参考百度图片页面解码,发现其实就是一个table,key和value对应就可以解码了,简直就是明文密码0.0.。。。

    所以在我们的程序里加个字典,多个解码的过程。

    期间解码过程一度出错,经过仔细排查,是url.translate(char_table)出了问题,

    str 的translate方法需要用单个字符的十进制unicode编码作为key 
    value 中的数字会被当成十进制unicode编码转换成字符 
    也可以直接用字符串作为value

    所以需要加上一行

    char_table = {ord(key): ord(value) for key, value in char_table.items()}

     然而试了还是不行,提示translate方法有问题,去查了一下官方的文档

    里面的例子是maketrans()方法一起用的,例子是替换元音字母

    #!/usr/bin/python
    
    from string import maketrans   # 引用 maketrans 函数。
    
    intab = "aeiou"
    outtab = "12345"
    trantab = maketrans(intab, outtab)
    
    str = "this is string example....wow!!!";
    print str.translate(trantab);

    输出结果:

    1 th3s 3s str3ng 2x1mpl2....w4w!!!

    所以对我们的chartable做处理,将key和value 通过maketrans关联:

        intab="wkv1ju2it3hs4g5rq6fp7eo8dn9cm0bla"
        outtab="abcdefghijklmnopqrstuvw1234567890"
        trantab = maketrans(intab, outtab)

    到此为止,成功解析图片url。

     4.爬取过程排错处理

    真正在爬取的时候,会有一些错误,什么404,403啊,这样的话,程序就爬不到图片,这就比较尴尬了。

    所以在下载的时候,需要做处理。

    开始的时候,我用的是这种:

    1 try:  
    2         res = requests.get(imgUrl, timeout=10)  #超时
    3         if str(res.status_code)[0] == "4":  
    4             print(str(res.status_code), ":" , imgUrl)  
    5             return False  
    6     except Exception as e:  
    7         print("抛出异常:", imgUrl)  
    8         print(e)

    但是爬着爬着发现,有的时候status_code不存在,又调试了一番,通过对比,发现404,403这种属于HTTPErro,但是request的时候,还可能产生URLError,如下图。

    所以抛出异常的过程就需要进行分开处理了。

    10060 因为目标主机主动拒绝,连接不能建立。这通常是因为试图连接到一个远程主机上不活动的服务,如没有服务器应用程序处于执行状态

     1 res = urllib2.Request(url)
     2         try:
     3             response = urllib2.urlopen(res ,data=None, timeout=5)  #超时处理
     4         except urllib2.URLError, e:
     5             if hasattr(e,'code'):
     6                 error_status = e.code
     7                 print(error_status, "未下载成功:", url)
     8                 continue
     9             elif hasattr(e,'reason'):
    10                 print( "time out", url)
    11                 continue

    测试成功,可以通过自己设置下载图片数量,然后无线爬了,爬的速度差不多2分钟爬了300多张,这和图片的源和大小都有一定关系。

    All Code:

      1 #coding: utf-8
      2 import os
      3 import re
      4 import urllib
      5 import urllib2
      6 import itertools  #迭代器
      7 from string import maketrans
      8 
      9 str_table = {
     10     '_z2C$q': ':',
     11     '_z&e3B': '.',
     12     'AzdH3F': '/'
     13 }
     14 
     15 char_table = {
     16     'w': 'a',
     17     'k': 'b',
     18     'v': 'c',
     19     '1': 'd',
     20     'j': 'e',
     21     'u': 'f',
     22     '2': 'g',
     23     'i': 'h',
     24     't': 'i',
     25     '3': 'j',
     26     'h': 'k',
     27     's': 'l',
     28     '4': 'm',
     29     'g': 'n',
     30     '5': 'o',
     31     'r': 'p',
     32     'q': 'q',
     33     '6': 'r',
     34     'f': 's',
     35     'p': 't',
     36     '7': 'u',
     37     'e': 'v',
     38     'o': 'w',
     39     '8': '1',
     40     'd': '2',
     41     'n': '3',
     42     '9': '4',
     43     'c': '5',
     44     'm': '6',
     45     '0': '7',
     46     'b': '8',
     47     'l': '9',
     48     'a': '0'
     49 }
     50 
     51 intab="wkv1ju2it3hs4g5rq6fp7eo8dn9cm0bla"
     52 outtab="abcdefghijklmnopqrstuvw1234567890"
     53 trantab = maketrans(intab, outtab)
     54 
     55 char_table = {ord(key): ord(value) for key, value in char_table.items()}
     56 def deCode(url):
     57     # 先替换字符串
     58     for key, value in str_table.items():
     59         url = url.replace(key, value)
     60     # 再替换剩下的字符
     61     d=url.translate(trantab)
     62     return d
     63 
     64 def getMoreURL(word):
     65     word = urllib.quote(word)
     66     url = r"http://image.baidu.com/search/acjson?tn=resultjson_com&ipn=rj&ct=201326592&fp=result&queryWord={word}" 
     67           r"&cl=2&lm=-1&ie=utf-8&oe=utf-8&st=-1&ic=0&word={word}&face=0&istype=2nc=1&pn={pn}&rn=30"
     68     urls = (url.format(word=word, pn=x) for x in itertools.count(start=0, step=30))
     69     #itertools.count 0开始,步长30,迭代
     70     return urls
     71 
     72 def getHtml(url):
     73     page=urllib.urlopen(url)
     74     html=page.read()
     75     return html
     76 
     77 #解析图片url解码
     78 def getImg(html):
     79     reg=r'"objURL":"(.*?)"'   #正则
     80     # 括号表示分组,将括号的内容捕获到分组当中
     81     #  这个括号也就可以匹配网页中图片的url了
     82     imgre=re.compile(reg)
     83     imageList=re.findall(imgre, html)
     84     imgUrls=[]
     85 
     86     for image in imageList:
     87         imgUrls.append(deCode(image))
     88 
     89     l=len(imgUrls)
     90     print l
     91     return imgUrls
     92 
     93 def downLoad(urls,path):
     94     global index
     95     for url in urls:
     96         print("Downloading:", url)
     97         res = urllib2.Request(url)
     98         try:
     99             response = urllib2.urlopen(res ,data=None, timeout=5)  #超时处理
    100         except urllib2.URLError, e:
    101             if hasattr(e,'code'):
    102                 error_status = e.code
    103                 print(error_status, "未下载成功:", url)
    104                 continue
    105             elif hasattr(e,'reason'):
    106                 print( "time out", url)
    107                 continue
    108 
    109             continue
    110 
    111         filename = os.path.join(path, str(index) + ".jpg")
    112         urllib.urlretrieve(url,filename)        #直接将远程数据下载到本地。
    113         index += 1
    114         # urllib.urlretrieve(url[, filename[, reporthook[, data]]])
    115         # 参数说明:
    116         # url:外部或者本地url
    117         # filename:指定了保存到本地的路径(如果未指定该参数,urllib会生成一个临时文件来保存数据);
    118         # reporthook:是一个回调函数,当连接上服务器、以及相应的数据块传输完毕的时候会触发该回调。我们可以利用这个回调函数来显示当前的下载进度。
    119         # data:指post到服务器的数据。该方法返回一个包含两个元素的元组(filename, headers),filename表示保存到本地的路径,header表示服务器的响应头。
    120         if index-1==1000:
    121             break
    122 
    123 if __name__ == '__main__':
    124     keyWord="新垣结衣"
    125     index = 1
    126     Savepath = "D:TestDownload"
    127     urls=getMoreURL(keyWord)
    128 
    129     for url in urls:
    130         downLoad(getImg(getHtml(url)),Savepath)
    131         if index-1==1000:
    132             break
    View Code
  • 相关阅读:
    Wireshark——工具
    Wireshark——网络协议
    Wireshark——过滤器
    Wireshark——数据包、着色规则和提示
    Floyd最小环
    有向环覆盖问题
    KM算法
    归并排序
    树状数组
    构造强连通图
  • 原文地址:https://www.cnblogs.com/SeekHit/p/6284974.html
Copyright © 2011-2022 走看看