Web Spider_2

网络爬虫入门教程(2)

接着上一篇博客,这篇博客会具体讲述网络爬虫代码的构建。

何为爬虫

相信很多人都听说过网络爬虫,但是就我的经验而言,很多人并不了解它究竟是什么。(问这是一种什么虫子的人 😂 )实际上网络爬虫只是在网络上收集资源的程序,简单的来说就是去网上批量下载各种资源,并且完成目标内容的提取。

代码编写大纲

  1. 想要去网上下载资源,首先得有URL,批量下载资源时URL不能简单的认为获取,而要从网上获取
  2. 虽然有了URL就可以访问网页,但是程序毕竟和浏览器不一样,所以得进行伪装
  3. 向服务器发起请求,下载资源
  4. 编码问题
    • 在网络传输数据时,由于是tcp协议,所以采用字节传输,因此传输的内容(string)要先转换成二进制,同样的,当我们从网上下载得到的信息要进行解码。编码常用的编码字符集有utf-8和gbk两种,在解码时要用编码时的字符集进行解码
    • URL中只能出现限定的字符,中文、特殊字符不能出现(浏览器地址栏可以显示中文,应该是自动解码了)因此URL在进行拼接完后必须进行编码,否则会报错

代码示例

  1. 最简单的爬虫

    1
    2
    3
    4
    5
    6
    7
    import urllib.request
    import urllib.parse

    url='http://www.baidu.com'
    response=urllib.request.urlopen(url)
    #其中decode()函数就是对获得的HTML内容进行解码
    print(response.read().decode())

    mark

    结果如图所示,我们就能得到百度的HTML文件,但是其实我们并没有成功的得到百度的HTML,在第三点中会讲到。

  2. 爬取百度贴吧

    打开百度贴吧,我们可以得到URL为[https://tieba.baidu.com](https://tieba.baidu.com/),然后我们输入“励志”,页面跳转,URL变为https://tieba.baidu.com/f?ie=utf-8&kw=励志&fr=search,改变搜索的词多试几次,我们会发现URL变化的只有kw=##这一项,于是我们可以知道##与搜索的内容有关。当然有的浏览器可能地址栏不显示中文(亲测edge显示编码后的内容,Chrome显示中文),但是有相同的规律,于是我们可以猜测##为搜索内容编码后的东西。我们可以通过站长工具这个网站在线进行URL的解码,发现##解码后就是搜索的内容。有时候我们会发现URL可能很长,这时我们可以删掉一些内容,用缩短后的URL进行访问,如果还能访问说明删除的那些参数是多于的,在这个例子中删除头和尾的参数并不影响正常访问,所以程序中的URL与浏览器地址栏中显示的并不一样

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import urllib.request
    import urllib.parse
    #原始URL
    url='https://tieba.baidu.com/'
    query_string=input('请输入要下载的贴吧:')
    #urlencode函数需要传递一个字典
    data={
    'kw':query_string
    }
    query_string=urllib.parse.urlencode(data)
    #URL参数前要加?
    url=url+'?'+query_string
    response=urllib.request.urlopen(url)
    print(response.read().decode())

    这个程序就能下载你想要的贴吧的HTML文件,经验证和在浏览器中搜索得到的是一样的

  3. 让代码伪装成服务器

    如果像上面那样写程序去访问服务器,有时候可能会被服务器拒绝(其实第一个示例中返回的HTML只有这么短是不正常的,出现这种情况就是因为服务器识破了爬虫的身份),因为,有的服务器可以知道我们不是浏览器而是爬虫程序,所以必须进行伪装,伪装的方法只需要在请求中包含User-Agent这一项,User-Agent表明访问者的身份。User-Agent可以在谷歌浏览器中F12,然后在http请求中找到,也可以自行百度。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import urllib.request
    import urllib.parse

    url='http://www.baidu.com'
    headers={
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36',
    }
    request=urllib.request.Request(url,headers=headers)
    response=urllib.request.urlopen(request)
    print(response.read().decode())

    运行这个程序,我们才会得到百度真正的HTML,如图:

    mark

  4. 一个综合型的示例,获取指定贴吧的指定页内容,包括了URL拼接、请求构建、文件的写入

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    import urllib.request
    import urllib.parse
    import os

    ba_name=input('请输入要查询的贴吧名')
    start_page=int(input('请输入起始页'))
    end_page=int(input('请输入结束页'))
    #创建用来保存的文件夹
    if not (os.path.exists(ba_name)):
    os.mkdir(ba_name)
    #URL中不变的内容
    url='http://tieba.baidu.com/f?ie=utf-8&'
    data={
    'kw':ba_name,
    }
    url+=urllib.parse.urlencode(data)
    for page in range(start_page,end_page+1):
    #此处str((page-1)*50)这个规律是通过观察的出的--在浏览器访问不同页数,观察URL的变化,在这个例子中起始页URL中pn=0,第二页pn=50,第三页pn=100...规律可得
    url_new=url+('&pn='+str((page-1)*50))
    # print(url_new)
    proxy=urllib.request.ProxyHandler()
    opener=urllib.request.build_opener(proxy)
    headers={
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36',
    }
    request=urllib.request.Request(url=url_new,headers=headers)
    print('第%s页开始下载...'% page)
    response= opener.open(request)
    filename=ba_name+'_'+ str(page) + '.html'
    filepath=ba_name+'/'+filename
    with open(filepath,'wb')as fp:
    fp.write(response.read())
    print('第%s页结束下载...'% page)

    运行这个程序我们就可以得到HTML文件(貌似不怎么完整,应该是百度搞了防盗措施),但是这个例子是很理想化的,在大多数情况下我们很有可能找不到页码变化和URL变化之间的规律

  5. 为了解决页码变化和URL变化“无联系”(联系肯定有,只是做了加密)的问题,我们可以从已经得到HTML中获取下一页的URL,因为在浏览器中我们去往下一页需要点击某“下一页”的按钮,如果了解HTML语言,我们就知道“下一页”按钮在HTML中就是一个a标签,而a标签中就会有下一页的URL,因为涉及到内容的解析,这只做说明,相关例子将在下一篇博客中给出。

  6. 正所谓道高一尺,魔高一丈,爬虫多了对服务器来说压力会很大,所以很多服务器会有反爬的措施,常见的有防盗链、ip检测、验证码等等,其中比较好处理的是ip检测。要想解决ip检测的问题首先你得有代理服务器(网上可租,也有免费的,哎,免费的不敢保证),通过代理的方式我们就能避免这个问题。代理这个概念在生活中很常见,代理就是找第三方帮你做事,也就是爬虫程序不直接向服务器发起请求,而是由代理服务器向服务器发起请求,然后再返回给爬虫程序,因此服务器不会知道你的ip,也就封不了。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    import urllib.request
    import urllib.parse
    url = 'http://www.baidu.com/'

    #设置代理,在ProxyHandler中传入代理ip加端口号
    proxy = urllib.request.ProxyHandler({'http': 'http://127.0.0.1:8888/'})

    #创建一个打开器,opener比urlopen强大得多,有很多urlopen做不到的功能,比如代理、验证cookie
    opener = urllib.request.build_opener(proxy)

    #install_opener会使得代理全局生效,即使用urlopen方法其实会调用创建的opener的open方法
    urllib.request.install_opener(opener)
    headers={
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36',

    }
    request=urllib.request.Request(url=url,headers=headers)
    response =urllib.request.urlopen(request)
    print(response)
  7. 对于需要登录的网站,如果我们采取上面的方法来下载HTML文件,虽然可能不报错,但是肯定获取不到HTML文件。这说明我们没有完全模仿浏览器,这时如果我们在谷歌浏览器中审查元素,把http协议中的请求头黏贴到headers中,再运行程序发现又可以获取到HTML了。其实在登录过程中,服务器的响应中会返回设置cookie的一个命令,然后浏览器就会保存这个cookie,作为登录过的凭证,每次浏览器访问登录后才能访问的内容时都要携带这个cookie服务器才会返回请求的资源,解决登录问题简单粗暴的方法就是在浏览器找到cookie粘贴到headers中,如

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    import urllib.request
    import urllib.parse

    url='http://www.renren.com/971983124/newsfeed/photo'
    headers={
    'user-agent': 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50',
    'Cookie': 'anonymid=jzjetqke-tjiod7; depovince=BJ; _r01_=1; JSESSIONID=abcj8ylTjQ-n3_4qbLTYw; ick_login=5929d602-a414-4f1b-b8fa-83f01844c486; t=fdfdca524711412440e3aa2f89ddd00a4; societyguester=fdfdca524711412440e3aa2f89ddd00a4; id=971983124; xnsid=526b61a2; ver=7.0; loginfrom=null; jebe_key=5d8a8bf3-cf7c-441b-bc60-65f236cb5832%7Cbf794041c970cd1607a6fc82acb47392%7C1566280296884%7C1%7C1566280297531; jebe_key=5d8a8bf3-cf7c-441b-bc60-65f236cb5832%7Cbf794041c970cd1607a6fc82acb47392%7C1566280296884%7C1%7C1566280297535; wp_fold=0; jebecookies=576b6d5f-c52d-4d79-a6a6-6724e7aa3b0a|||||'
    }
    request=urllib.request.Request(url,headers=headers)
    response=urllib.request.urlopen(request)

    with open('renren.html','wb')as fp:
    fp.write(response.read())

    当然这个cookie早已过期,现在运行无法得到任何东西


    限于篇幅,更好的解决cookie的方法将在下一篇博客中给出

文章目录
  1. 1. 网络爬虫入门教程(2)
    1. 1.1. 何为爬虫
    2. 1.2. 代码编写大纲
    3. 1.3. 代码示例