精华内容
参与话题
问答
  • 手把手教你利用爬虫网页(Python代码)

    万次阅读 多人点赞 2019-05-14 14:34:48
    本文主要分为两个部分:一部分是网络爬虫的概述,帮助大家详细了解网络爬虫;另一部分是HTTP请求的Python实现,帮助大家了解Python中实现HTTP请求的各种方式,以...
        

    640?wx_fmt=jpeg

    本文主要分为两个部分:一部分是网络爬虫的概述,帮助大家详细了解网络爬虫;另一部分是HTTP请求的Python实现,帮助大家了解Python中实现HTTP请求的各种方式,以便具备编写HTTP网络程序的能力。


    01

    网络爬虫概述


    接下来从网络爬虫的概念、用处与价值和结构等三个方面,让大家对网络爬虫有一个基本的了解。

    1. 网络爬虫及其应用

    随着网络的迅速发展,万维网成为大量信息的载体,如何有效地提取并利用这些信息成为一个巨大的挑战,网络爬虫应运而生。网络爬虫(又被称为网页蜘蛛、网络机器人),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。下面通过图3-1展示一下网络爬虫在互联网中起到的作用:


    640?wx_fmt=png

    ▲图3-1 网络爬虫


    网络爬虫按照系统结构和实现技术,大致可以分为以下几种类型:通用网络爬虫、聚焦网络爬虫、增量式网络爬虫、深层网络爬虫。实际的网络爬虫系统通常是几种爬虫技术相结合实现的。

    搜索引擎(Search Engine),例如传统的通用搜索引擎baidu、Yahoo和Google等,是一种大型复杂的网络爬虫,属于通用性网络爬虫的范畴。但是通用性搜索引擎存在着一定的局限性:

    1. 不同领域、不同背景的用户往往具有不同的检索目的和需求,通用搜索引擎所返回的结果包含大量用户不关心的网页。

    2. 通用搜索引擎的目标是尽可能大的网络覆盖率,有限的搜索引擎服务器资源与无限的网络数据资源之间的矛盾将进一步加深。

    3. 万维网数据形式的丰富和网络技术的不断发展,图片、数据库、音频、视频多媒体等不同数据大量出现,通用搜索引擎往往对这些信息含量密集且具有一定结构的数据无能为力,不能很好地发现和获取。

    4. 通用搜索引擎大多提供基于关键字的检索,难以支持根据语义信息提出的查询。

    为了解决上述问题,定向抓取相关网页资源的聚焦爬虫应运而生。

    聚焦爬虫是一个自动下载网页的程序,它根据既定的抓取目标,有选择地访问万维网上的网页与相关的链接,获取所需要的信息。与通用爬虫不同,聚焦爬虫并不追求大的覆盖,而将目标定为抓取与某一特定主题内容相关的网页,为面向主题的用户查询准备数据资源。

    说完了聚焦爬虫,接下来再说一下增量式网络爬虫。增量式网络爬虫是指对已下载网页采取增量式更新和只爬行新产生的或者已经发生变化网页的爬虫,它能够在一定程度上保证所爬行的页面是尽可能新的页面。

    和周期性爬行和刷新页面的网络爬虫相比,增量式爬虫只会在需要的时候爬行新产生或发生更新的页面,并不重新下载没有发生变化的页面,可有效减少数据下载量,及时更新已爬行的网页,减小时间和空间上的耗费,但是增加了爬行算法的复杂度和实现难度。

    例如:想获取赶集网的招聘信息,以前爬取过的数据没有必要重复爬取,只需要获取更新的招聘数据,这时候就要用到增量式爬虫。

    最后说一下深层网络爬虫。Web页面按存在方式可以分为表层网页和深层网页。表层网页是指传统搜索引擎可以索引的页面,以超链接可以到达的静态网页为主构成的Web页面。深层网络是那些大部分内容不能通过静态链接获取的、隐藏在搜索表单后的,只有用户提交一些关键词才能获得的Web页面。

    例如用户登录或者注册才能访问的页面。可以想象这样一个场景:爬取贴吧或者论坛中的数据,必须在用户登录后,有权限的情况下才能获取完整的数据。

    2. 网络爬虫结构

    下面用一个通用的网络爬虫结构来说明网络爬虫的基本工作流程,如图3-4所示。


    640?wx_fmt=png

    ▲图3-4 网络爬虫结构

    网络爬虫的基本工作流程如下:

    1. 首先选取一部分精心挑选的种子URL。

    2. 将这些URL放入待抓取URL队列。

    3. 从待抓取URL队列中读取待抓取队列的URL,解析DNS,并且得到主机的IP,并将URL对应的网页下载下来,存储进已下载网页库中。此外,将这些URL放进已抓取URL队列。

    4. 分析已抓取URL队列中的URL,从已下载的网页数据中分析出其他URL,并和已抓取的URL进行比较去重,最后将去重过的URL放入待抓取URL队列,从而进入下一个循环。

    02

    HTTP请求的Python实现


    通过上面的网络爬虫结构,我们可以看到读取URL、下载网页是每一个爬虫必备而且关键的功能,这就需要和HTTP请求打交道。接下来讲解Python中实现HTTP请求的三种方式:urllib2/urllib、httplib/urllib以及Requests。

    1. urllib2/urllib实现

    urllib2和urllib是Python中的两个内置模块,要实现HTTP功能,实现方式是以urllib2为主,urllib为辅。

    1.1 首先实现一个完整的请求与响应模型

    urllib2提供一个基础函数urlopen,通过向指定的URL发出请求来获取数据。最简单的形式是:

    import urllib2
    response=urllib2.urlopen('http://www.zhihu.com')
    html=response.read()
    print html

    其实可以将上面对http://www.zhihu.com的请求响应分为两步,一步是请求,一步是响应,形式如下:

    import urllib2
    # 请求
    request=urllib2.Request('http://www.zhihu.com')
    # 响应
    response = urllib2.urlopen(request)
    html=response.read()
    print html

    上面这两种形式都是GET请求,接下来演示一下POST请求,其实大同小异,只是增加了请求数据,这时候用到了urllib。示例如下:

    import urllib
    import urllib2
    url = 'http://www.xxxxxx.com/login'
    postdata = {'username' : 'qiye',
        'password' : 'qiye_pass'}
    # info 需要被编码为urllib2能理解的格式,这里用到的是urllib
    data = urllib.urlencode(postdata)
    req = urllib2.Request(url, data)
    response = urllib2.urlopen(req)
    html = response.read()

    但是有时会出现这种情况:即使POST请求的数据是对的,但是服务器拒绝你的访问。这是为什么呢?问题出在请求中的头信息,服务器会检验请求头,来判断是否是来自浏览器的访问,这也是反爬虫的常用手段。

    1.2 请求头headers处理

    将上面的例子改写一下,加上请求头信息,设置一下请求头中的User-Agent域和Referer域信息。

    import urllib
    import urllib2
    url = 'http://www.xxxxxx.com/login'
    user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
    referer='http://www.xxxxxx.com/'
    postdata = {'username' : 'qiye',
        'password' : 'qiye_pass'}
    # 将user_agent,referer写入头信息
    headers={'User-Agent':user_agent,'Referer':referer}
    data = urllib.urlencode(postdata)
    req = urllib2.Request(url, data,headers)
    response = urllib2.urlopen(req)
    html = response.read()

    也可以这样写,使用add_header来添加请求头信息,修改如下:

    import urllib
    import urllib2
    url = 'http://www.xxxxxx.com/login'
    user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
    referer='http://www.xxxxxx.com/'
    postdata = {'username' : 'qiye',
        'password' : 'qiye_pass'}
    data = urllib.urlencode(postdata)
    req = urllib2.Request(url)
    # 将user_agent,referer写入头信息
    req.add_header('User-Agent',user_agent)
    req.add_header('Referer',referer)
    req.add_data(data)
    response = urllib2.urlopen(req)
    html = response.read()

    对有些header要特别留意,服务器会针对这些header做检查,例如:

    • User-Agent:有些服务器或Proxy会通过该值来判断是否是浏览器发出的请求。

    • Content-Type:在使用REST接口时,服务器会检查该值,用来确定HTTP Body中的内容该怎样解析。在使用服务器提供的RESTful或SOAP服务时,Content-Type设置错误会导致服务器拒绝服务。常见的取值有:application/xml(在XML RPC,如RESTful/SOAP调用时使用)、application/json(在JSON RPC调用时使用)、application/x-www-form-urlencoded(浏览器提交Web表单时使用)。

    • Referer:服务器有时候会检查防盗链。

    1.3 Cookie处理

    urllib2对Cookie的处理也是自动的,使用CookieJar函数进行Cookie的管理。如果需要得到某个Cookie项的值,可以这么做:

    import urllib2
    import cookielib
    cookie = cookielib.CookieJar()
    opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie))
    response = opener.open('http://www.zhihu.com')
    for item in cookie:
        print item.name+':'+item.value

    但是有时候会遇到这种情况,我们不想让urllib2自动处理,我们想自己添加Cookie的内容,可以通过设置请求头中的Cookie域来做:

    import  urllib2
    opener = urllib2.build_opener()
    opener.addheaders.append( ( 'Cookie''email=' + "xxxxxxx@163.com" ) )
    req = urllib2.Request( "http://www.zhihu.com/" )
    response = opener.open(req)
    print response.headers
    retdata = response.read()

    1.4 Timeout设置超时

    在Python2.6之前的版本,urllib2的API并没有暴露Timeout的设置,要设置Timeout值,只能更改Socket的全局Timeout值。示例如下:

    import urllib2
    import socket
    socket.setdefaulttimeout(10# 10 秒钟后超时
    urllib2.socket.setdefaulttimeout(10# 另一种方式

    在Python2.6及新的版本中,urlopen函数提供了对Timeout的设置,示例如下:

    import urllib2
    request=urllib2.Request('http://www.zhihu.com')
    response = urllib2.urlopen(request,timeout=2)
    html=response.read()
    print html

    1.5 获取HTTP响应码

    对于200 OK来说,只要使用urlopen返回的response对象的getcode()方法就可以得到HTTP的返回码。但对其他返回码来说,urlopen会抛出异常。这时候,就要检查异常对象的code属性了,示例如下:

    import urllib2
    try:
        response = urllib2.urlopen('http://www.google.com')
        print response
    except urllib2.HTTPError as e:
        if hasattr(e, 'code'):
            print 'Error code:',e.code

    1.6 重定向

    urllib2默认情况下会针对HTTP 3XX返回码自动进行重定向动作。要检测是否发生了重定向动作,只要检查一下Response的URL和Request的URL是否一致就可以了,示例如下:

    import urllib2
    response = urllib2.urlopen('http://www.zhihu.cn')
    isRedirected = response.geturl() == 'http://www.zhihu.cn'

    如果不想自动重定向,可以自定义HTTPRedirectHandler类,示例如下:

    import urllib2
    class RedirectHandler(urllib2.HTTPRedirectHandler):
        def http_error_301(self, req, fp, code, msg, headers):
            pass
        def http_error_302(self, req, fp, code, msg, headers):
            result = urllib2.HTTPRedirectHandler.http_error_301(self, req, fp, code, 
            msg, headers)
            result.status = code
            result.newurl = result.geturl()
            return result
    opener = urllib2.build_opener(RedirectHandler)
    opener.open('http://www.zhihu.cn')

    1.7 Proxy的设置

    在做爬虫开发中,必不可少地会用到代理。urllib2默认会使用环境变量http_proxy来设置HTTP Proxy。但是我们一般不采用这种方式,而是使用ProxyHandler在程序中动态设置代理,示例代码如下:

    import urllib2
    proxy = urllib2.ProxyHandler({'http''127.0.0.1:8087'})
    opener = urllib2.build_opener([proxy,])
    urllib2.install_opener(opener)
    response = urllib2.urlopen('http://www.zhihu.com/')
    print response.read()

    这里要注意的一个细节,使用urllib2.install_opener()会设置urllib2的全局opener,之后所有的HTTP访问都会使用这个代理。这样使用会很方便,但不能做更细粒度的控制,比如想在程序中使用两个不同的Proxy设置,这种场景在爬虫中很常见。比较好的做法是不使用install_opener去更改全局的设置,而只是直接调用opener的open方法代替全局的urlopen方法,修改如下:

    import urllib2
    proxy = urllib2.ProxyHandler({'http''127.0.0.1:8087'})
    opener = urllib2.build_opener(proxy,)
    response = opener.open("http://www.zhihu.com/")
    print response.read()


    2. httplib/urllib实现

    httplib模块是一个底层基础模块,可以看到建立HTTP请求的每一步,但是实现的功能比较少,正常情况下比较少用到。在Python爬虫开发中基本上用不到,所以在此只是进行一下知识普及。下面介绍一下常用的对象和函数:


    • 创建HTTPConnection对象:

      class httplib.HTTPConnection(host[, port[, strict[, timeout[, source_address]]]])。

    • 发送请求:

      HTTPConnection.request(method, url[, body[, headers]])。

    • 获得响应:

      HTTPConnection.getresponse()。

    • 读取响应信息:

      HTTPResponse.read([amt])。

    • 获得指定头信息:

      HTTPResponse.getheader(name[, default])。

    • 获得响应头(header, value)元组的列表:

      HTTPResponse.getheaders()。

    • 获得底层socket文件描述符:

      HTTPResponse.fileno()。

    • 获得头内容:

      HTTPResponse.msg。

    • 获得头http版本:

      HTTPResponse.version。

    • 获得返回状态码:

      HTTPResponse.status。

    • 获得返回说明:

      HTTPResponse.reason。

    接下来演示一下GET请求和POST请求的发送,首先是GET请求的示例,如下所示:

    import httplib
    conn =None
    try:
        conn = httplib.HTTPConnection("www.zhihu.com")
        conn.request("GET""/")
        response = conn.getresponse()
        print response.status, response.reason
        print '-' * 40
        headers = response.getheaders()
        for h in headers:
            print h
        print '-' * 40
        print response.msg
    except Exception,e:
        print e
    finally:
        if conn:
            conn.close()

    POST请求的示例如下:

    import httplib, urllib
    conn = None
    try:
        params = urllib.urlencode({'name''qiye''age'22})
        headers = {"Content-type""application/x-www-form-urlencoded"
        , "Accept""text/plain"}
        conn = httplib.HTTPConnection("www.zhihu.com"80, timeout=3)
        conn.request("POST""/login", params, headers)
        response = conn.getresponse()
        print response.getheaders() # 获取头信息
        print response.status
        print response.read()
    except Exception, e:
        print e
        finally:
        if conn:
            conn.close()

    3. 更人性化的Requests

    Python中Requests实现HTTP请求的方式,是本人极力推荐的,也是在Python爬虫开发中最为常用的方式。Requests实现HTTP请求非常简单,操作更加人性化。

    Requests库是第三方模块,需要额外进行安装。Requests是一个开源库,源码位于:

    GitHub: https://github.com/kennethreitz/requests

    希望大家多多支持作者。

    使用Requests库需要先进行安装,一般有两种安装方式:

    • 使用pip进行安装,安装命令为:pip install requests,不过可能不是最新版。

    • 直接到GitHub上下载Requests的源代码,下载链接为:

      https://github.com/kennethreitz/requests/releases

      将源代码压缩包进行解压,然后进入解压后的文件夹,运行setup.py文件即可。

    如何验证Requests模块安装是否成功呢?在Python的shell中输入import requests,如果不报错,则是安装成功。如图3-5所示。

    640?wx_fmt=png

    ▲图3-5 验证Requests安装

    3.1 首先还是实现一个完整的请求与响应模型

    以GET请求为例,最简单的形式如下:

    import requests
    r = requests.get('http://www.baidu.com')
    print r.content

    大家可以看到比urllib2实现方式的代码量少。接下来演示一下POST请求,同样是非常简短,更加具有Python风格。示例如下:

    import requests
    postdata={'key':'value'}
    r = requests.post('http://www.xxxxxx.com/login',data=postdata)
    print r.content

    HTTP中的其他请求方式也可以用Requests来实现,示例如下:

    r = requests.put('http://www.xxxxxx.com/put', data = {'key':'value'})
    r = requests.delete('http://www.xxxxxx.com/delete')
    r = requests.head('http://www.xxxxxx.com/get')
    r = requests.options('http://www.xxxxxx.com/get')

    接着讲解一下稍微复杂的方式,大家肯定见过类似这样的URL:

    http://zzk.cnblogs.com/s/blogpost?Keywords=blog:qiyeboy&pageindex=1

    就是在网址后面紧跟着“?”,“?”后面还有参数。那么这样的GET请求该如何发送呢?肯定有人会说,直接将完整的URL带入即可,不过Requests还提供了其他方式,示例如下:

    import requests
        payload = {'Keywords''blog:qiyeboy','pageindex':1}
    r = requests.get('http://zzk.cnblogs.com/s/blogpost', params=payload)
    print r.url

    通过打印结果,我们看到最终的URL变成了:

    http://zzk.cnblogs.com/s/blogpost?Keywords=blog:qiyeboy&pageindex=1

    3.2 响应与编码

    还是从代码入手,示例如下:

    import requests
    r = requests.get('http://www.baidu.com')
    print 'content-->'+r.content
    print 'text-->'+r.text
    print 'encoding-->'+r.encoding
    r.encoding='utf-8'
    print 'new text-->'+r.text

    其中r.content返回的是字节形式,r.text返回的是文本形式,r.encoding返回的是根据HTTP头猜测的网页编码格式。

    输出结果中:“text-->”之后的内容在控制台看到的是乱码,“encoding-->”之后的内容是ISO-8859-1(实际上的编码格式是UTF-8),由于Requests猜测编码错误,导致解析文本出现了乱码。Requests提供了解决方案,可以自行设置编码格式,r.encoding='utf-8'设置成UTF-8之后,“new text-->”的内容就不会出现乱码。

    但是这种手动的方式略显笨拙,下面提供一种更加简便的方式:chardet,这是一个非常优秀的字符串/文件编码检测模块。安装方式如下:

    pip install chardet

    安装完成后,使用chardet.detect()返回字典,其中confidence是检测精确度,encoding是编码形式。示例如下:

    import requests
    r = requests.get('http://www.baidu.com')
    print chardet.detect(r.content)
    r.encoding = chardet.detect(r.content)['encoding']
    print r.text

    直接将chardet探测到的编码,赋给r.encoding实现解码,r.text输出就不会有乱码了。

    除了上面那种直接获取全部响应的方式,还有一种流模式,示例如下:

    import requests
    r = requests.get('http://www.baidu.com',stream=True)
    print r.raw.read(10)

    设置stream=True标志位,使响应以字节流方式进行读取,r.raw.read函数指定读取的字节数。

    3.3 请求头headers处理

    Requests对headers的处理和urllib2非常相似,在Requests的get函数中添加headers参数即可。示例如下:

    import requests
    user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
    headers={'User-Agent':user_agent}
    r = requests.get('http://www.baidu.com',headers=headers)
    print r.content

    3.4 响应码code和响应头headers处理

    获取响应码是使用Requests中的status_code字段,获取响应头使用Requests中的headers字段。示例如下:

    import requests
    r = requests.get('http://www.baidu.com')
    if r.status_code == requests.codes.ok:
        print r.status_code# 响应码
        print r.headers# 响应头
        print r.headers.get('content-type')# 推荐使用这种获取方式,获取其中的某个字段
        print r.headers['content-type']# 不推荐使用这种获取方式
    else:
        r.raise_for_status()

    上述程序中,r.headers包含所有的响应头信息,可以通过get函数获取其中的某一个字段,也可以通过字典引用的方式获取字典值,但是不推荐,因为如果字段中没有这个字段,第二种方式会抛出异常,第一种方式会返回None。

    r.raise_for_status()是用来主动地产生一个异常,当响应码是4XX或5XX时,raise_for_status()函数会抛出异常,而响应码为200时,raise_for_status()函数返回None。

    3.5 Cookie处理

    如果响应中包含Cookie的值,可以如下方式获取Cookie字段的值,示例如下:

    import requests
    user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
    headers={'User-Agent':user_agent}
    r = requests.get('http://www.baidu.com',headers=headers)
    # 遍历出所有的cookie字段的值
    for cookie in r.cookies.keys():
        print cookie+':'+r.cookies.get(cookie)

    如果想自定义Cookie值发送出去,可以使用以下方式,示例如下:

    import requests
    user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
    headers={'User-Agent':user_agent}
    cookies = dict(name='qiye',age='10')
    r = requests.get('http://www.baidu.com',headers=headers,cookies=cookies)
    print r.text

    还有一种更加高级,且能自动处理Cookie的方式,有时候我们不需要关心Cookie值是多少,只是希望每次访问的时候,程序自动把Cookie的值带上,像浏览器一样。Requests提供了一个session的概念,在连续访问网页,处理登录跳转时特别方便,不需要关注具体细节。使用方法示例如下:

    import Requests
    oginUrl = 'http://www.xxxxxxx.com/login'
    s = requests.Session()
    #首先访问登录界面,作为游客,服务器会先分配一个cookie
    r = s.get(loginUrl,allow_redirects=True)
    datas={'name':'qiye','passwd':'qiye'}
    #向登录链接发送post请求,验证成功,游客权限转为会员权限
    r = s.post(loginUrl, data=datas,allow_redirects= True)
    print r.text

    上面的这段程序,其实是正式做Python开发中遇到的问题,如果没有第一步访问登录的页面,而是直接向登录链接发送Post请求,系统会把你当做非法用户,因为访问登录界面时会分配一个Cookie,需要将这个Cookie在发送Post请求时带上,这种使用Session函数处理Cookie的方式之后会很常用。

    3.6 重定向与历史信息

    处理重定向只是需要设置一下allow_redirects字段即可,例如:

    r=requests.get('http://www.baidu.com',allow_redirects=True)

    将allow_redirects设置为True,则是允许重定向;设置为False,则是禁止重定向。如果是允许重定向,可以通过r.history字段查看历史信息,即访问成功之前的所有请求跳转信息。示例如下:

    import requests
    r = requests.get('http://github.com')
    print r.url
    print r.status_code
    print r.history

    打印结果如下:

    https://github.com/
    200
    (<Response [301]>,)

    上面的示例代码显示的效果是访问GitHub网址时,会将所有的HTTP请求全部重定向为HTTPS。

    3.7 超时设置

    超时选项是通过参数timeout来进行设置的,示例如下:

    requests.get('http://github.com', timeout=2)

    3.8 代理设置

    使用代理Proxy,你可以为任意请求方法通过设置proxies参数来配置单个请求:

    import requests
    proxies = {
        "http""http://0.10.1.10:3128",
        "https""http://10.10.1.10:1080",
    }
    requests.get("http://example.org", proxies=proxies)

    也可以通过环境变量HTTP_PROXY和HTTPS_PROXY?来配置代理,但是在爬虫开发中不常用。你的代理需要使用HTTP Basic Auth,可以使用http://user:password@host/语法:

    proxies = {
        "http""http://user:pass@10.10.1.10:3128/",
    }


    03

    小结


    本文主要讲解了网络爬虫的结构和应用,以及Python实现HTTP请求的几种方法。希望大家对本文中的网络爬虫工作流程和Requests实现HTTP请求的方式重点吸收消化。

    本文摘编自《Python爬虫开发与项目实战》,经出版方授权发布。

    关于作者:范传辉,资深网虫,Python开发者,参与开发了多项网络应用,在实际开发中积累了丰富的实战经验,并善于总结,贡献了多篇技术文章广受好评。研究兴趣是网络安全、爬虫技术、数据分析、驱动开发等技术。

    640?wx_fmt=jpeg

    Python爬虫开发与项目实战

    扫码购买

    640?wx_fmt=png


    本书特色:

    • 由浅入深,从Python和Web前端基础开始讲起,逐步加深难度,层层递进。

    • 内容详实,从静态网站到动态网站,从单机爬虫到分布式爬虫,既包含基础知识点,又讲解了关键问题和难点分析,方便读者完成进阶。

    • 实用性强,本书共有9个爬虫项目,以系统的实战项目为驱动,由浅及深地讲解爬虫开发中所需的知识和技能。

    • 难点详析,对js加密的分析、反爬虫措施的突破、去重方案的设计、分布式爬虫的开发进行了细致的讲解。

    扫码购买

    640?wx_fmt=png

    展开全文
  • 第一个爬虫实例-简单抓取网页内容

    千次阅读 2017-09-22 21:18:13
    通过以上各种各样的坑,也就是完成以下2个软件的安装,和另外若干包的安装后,终于可以爬虫一个实例。 新手关于python2.7.11 与 IDE pycharm的安装于使用 python下安装easy_install、pip遇到的问题 即...

    通过以上各种各样的坑,也就是完成以下2个软件的安装,和另外若干包的安装后,终于可以爬虫一个实例。

    新手关于python2.7.11 与 IDE pycharm的安装于使用

    python下安装easy_install、pip遇到的问题


    即爬虫一个网站的页面所有文字为例子.

    以下内容是此刻 糗事百科的部分截取内容:


    ……

    pycharm中运行以下程序,便有运行结果


    import requests
    from bs4 import BeautifulSoup
    
    
    content = requests.get('http://www.qiushibaike.com').content
    soup = BeautifulSoup(content, 'html.parser')
    
    for div in soup.find_all('div', {'class': 'content'}):
       print div.text.strip()
    

    运行结果:

    前段时间工作不忙,请了二十天假回老家,同龄人要么都结婚了,要么都不在家,我这一天除了吃饭睡觉,就是陪老爸老妈聊天看电视,再不就是去湖边瞎溜达,转眼假期满了,准备回程上班,因为早上五点就要赶车,老妈四点钟就起来,给我煮了好多鸡蛋还有早准备好的特产让我带走,因为太早就没叫老爸起床,老妈送我上车前说,二十天过的真快,习惯了你在家了,你却要走了,心里一下子空落落的,特不好受,老妈说的我心里也不好受,到下午时老爸打电话说,你走怎么不叫我,我现在才知道你走了。写这么多,不为别的,儿行千里,母担忧,有时间多



    查看全文
    多年以前经常在广西各个城市出差,宾馆里床头都会有小名片,只有电话没地址哪种,闲的无聊,就收集、然后把柳州的放在玉林,把玉林的放到来宾…
    上学时有天去网吧上网,一下玩忘了时间,到家都八点多了,正想着进门怎么跟父母扯谎,忽然听到屋内父母在和人说话,心想家里有人应该不会打我了。打开门一看,卧槽!班主任家访
    标题留给你们![挖鼻孔][挖鼻孔][挖鼻孔]
    果果姥姥刚刚抱回来一个大葫芦、准备包饺子!果果看见了、惊呼!哇……大梨!!!我说果果、那是葫芦!果果问我、那里面有葫芦娃吗?我无奈的说有、是六娃……于是果果在院子里找了一下午六娃
    懂么??嗯哼
    我真的被吓到了
    感谢2+2给孩子们带来的快乐![玫瑰][玫瑰]!替孩子们谢谢漂亮姐姐们和帅哥哥们的爱心蛋糕和月饼!又是一年中秋佳节……感谢各位,感谢阿依,
    大儿子四岁,很是贪玩总吵闹着让我给买玩具,可是家里玩具那么多,就没想买!,奈何儿子吵的实在太烦人了,终于在儿子的压迫下我想到了一条妙计..........那天我翻箱倒柜找出多年前坏掉的VR手柄,故意在儿子面前摆弄了一会,突然大喊(奥特曼变身!),大儿子瞬间就惊呆了,一副爸爸是英雄的表情,我喊完假装发现儿子,连忙的把手柄藏在身后,装作若无其事的走开了,从此以后大儿子再也没吵闹着要买玩具,而是围在我身旁要奥特曼变身器,我以后事情已经过去了,可是我万万没想到..................



    查看全文
    抓不住时间的我,总是眼睁睁看它溜走,世界上年轻的人到处有,为何我已经老了。为了生活孤军奋斗,早就吃够了生活的苦,世界上靠颜吃饭的人到处有,为何我是这么的丑……致我即将到来的30岁生日
    再说一个愚人节的吧,高中的时候寝室每一层都有一个寝室管理员,每天早上六点半准时叫你起床上早自习~~~~~高一那年愚人节的早上宿管老师的门被上了锁出不来,急得不能行就给楼下宿管打电话:陈老师来给我开下门,我让锁进屋里了。陈老师:别叫我,我也让锁进去了,哦对了三楼的也出不来!!
    ,,,,哈哈哈哈哈哈
    师傅:“徒弟,你初吻是什么感觉?”徒弟:“脸红,烫了一整天!”师傅:“哈哈,没想到你丫还害羞!”徒弟:“我没害羞,只是对方比较害羞了,扇了我十几个耳刮子……”师傅:“跟你说过多少次了,你就是不听,女人的劲能小点……”
    #伤感与生俱来#今天有个朋友跟我说,他昨晚很晚睡不着,就想如果他突然有两千万了该怎么分,一个一个分,等想起我的时候,他说他觉得给我多了,我肯定不要,所以最后决定给我两百!两百!!两千万就给我两百?!然后我手一抖,就给他拉黑了。
    明明是拉稀,还来放个屁,走在大街上,我好蓝瘦,香菇。
    出差几天回来恰逢老婆大姨妈来了,一直憋着,你懂的。今天称体重,发现比出差前重了一斤,老婆说外面的饭比家里的好吃呀,为表忠心,我说,要不是你大姨妈来让我射一次这一斤就没了[哈哈][哈哈],老婆白了我一眼说,切,就你那玩意儿剁下来称都没有一斤重,卧槽,伤自尊了哦,你特么是有多嫌弃,匿了[大哭][大哭][大哭]
    好友中午带来生鱼片,让我尝尝,吃了一片,好友问我怎么样?感觉挺好吃的,凉凉的…问她是不是芥末过期了,味道有点像薄荷!好友点点头:嗯,忘记买芥末了,放了点薄荷牙膏!我瞪了她一眼:牙膏能吃啊?好友幽幽的说:所以才让你先尝尝…我。。。
    大神们,告诉我这是啥
    最近有些冷落老公了,他说我再这样就要失去他了,突然我就急了,“是不是有人撩你?有的话,我灭她全家,诛她九族!”老公不紧不慢地问我“你先告诉我,九族是哪九族?”我。。。“一族,二族,三族,四族,五族,六族,七族,八族,九族”哼,还想难住我,好歹你媳妇我是人民教师呢
    布厂的人就可以这么任性这么浪费吗?上个厕所都用布,你要知道这么大一块布捐给山区孩子们够他们一个人上好几次厕所了!
    有一天,闺蜜和男朋友吵架,一直在哭。看的我心里那个纠结的就说:好歹你也叫你男朋友买辆宝马坐在里面哭,不然都对不起你那眼泪。我那傻闺蜜说:我们这里有共享宝马吗?
    在卧室看“小电影”的时候我哥突然进来了,吓的我把手机往旁边一扔,赶紧闭上了眼睛。我哥看到以后笑着说了句:“哎呦,还学会装死了?”我没理他,继续装睡。结果这时我哥看到我的手机还在放着“小电影”,然后大喊了一声:“卧槽!你这死的真特么龌龊!”
    刚才坐地铁二号线,因为是第二站所以有几个空位子,因为路很远就坐下去了!第三站就上了很多人,我是个别人站着自己不好意思坐着的人!可是虽然没有座位了可是也没有需要让座的!又过了一站~上来一个带着四五岁的孩子女士,然后本人马上站了起来让座,那个女的连说~谢谢,谢谢!虽然是应该的,可是心里真的挺暖的!也不好意思站在旁边,因为怕她难为情,就走到一对情侣面前站着了,又过了两站车上人可以说就很多了,这时候又上来一个抱着能有两三岁的女的,应该是孩子的奶奶~车开了之后这个抱孩子的女的跟那对情侣说:能不能给我让个座



    查看全文
    公司上班要求统一服装定制工作服,第二天上班看到清单上谁的谁的尺码。看到我尺码我当时就震惊了,我都没说怎么会知道我的尺寸呢,太不可思议了吧!后面写着楼主最大尺码。。。
    你可以嘲笑现在的我,但你决定不了以后的我  求神评


    Process finished with exit code 0



    完美!!!!!!

    展开全文
  • Python3网络爬虫(一):利用urllib进行简单的网页抓取

    万次阅读 多人点赞 2017-02-28 23:32:35
    运行平台:Windows10 Python版本:Python3.x IDE:Sublime text3

    运行平台:Windows
    Python版本:Python3.x
    IDE:Sublime text3

    转载请注明作者和出处:http://blog.csdn.net/c406495762/article/details/58716886

        一直想学习Python爬虫的知识,在网上搜索了一下,大部分都是基于Python2.x的。因此打算写一个Python3.x的爬虫笔记,以便后续回顾,欢迎一起交流、共同进步。

    一、预备知识

    1.Python3.x基础知识学习:

        可以在通过如下方式进行学习:

        (1)廖雪峰Python3教程(文档):

        URL:http://www.liaoxuefeng.com/

        (2)菜鸟教程Python3教程(文档):

        URL:http://www.runoob.com/python3/python3-tutorial.html

        (3)鱼C工作室Python教程(视频):

        小甲鱼老师很厉害,讲课风格幽默诙谐,如果时间充裕可以考虑看视频。

        URL:http://www.fishc.com/

    2.开发环境搭建:

        Sublime text3搭建Pyhthon IDE可以查看博客:

        URL:http://www.cnblogs.com/nx520zj/p/5787393.html

        URL:http://blog.csdn.net/c406495762/article/details/56489253

    二、网络爬虫的定义

        网络爬虫,也叫网络蜘蛛(Web Spider),如果把互联网比喻成一个蜘蛛网,Spider就是一只在网上爬来爬去的蜘蛛。网络爬虫就是根据网页的地址来寻找网页的,也就是URL。举一个简单的例子,我们在浏览器的地址栏中输入的字符串就是URL,例如:https://www.baidu.com/

        URL就是同意资源定位符(Uniform Resource Locator),它的一般格式如下(带方括号[]的为可选项):

        protocol :// hostname[:port] / path / [;parameters][?query]#fragment

        URL的格式由三部分组成:

        (1)protocol:第一部分就是协议,例如百度使用的就是https协议;

        (2)hostname[:port]:第二部分就是主机名(还有端口号为可选参数),一般网站默认的端口号为80,例如百度的主机名就是www.baidu.com,这个就是服务器的地址;

        (3)path:第三部分就是主机资源的具体地址,如目录和文件名等。

        网络爬虫就是根据这个URL来获取网页信息的。

    三、简单爬虫实例

        在Python3.x中,我们可以使用urlib这个组件抓取网页,urllib是一个URL处理包,这个包中集合了一些处理URL的模块,如下:

    picture1

    1.urllib.request模块是用来打开和读取URLs的;

    2.urllib.error模块包含一些有urllib.request产生的错误,可以使用try进行捕捉处理;

    3.urllib.parse模块包含了一些解析URLs的方法;

    4.urllib.robotparser模块用来解析robots.txt文本文件.它提供了一个单独的RobotFileParser类,通过该类提供的can_fetch()方法测试爬虫是否可以下载一个页面。

        我们使用urllib.request.urlopen()这个接口函数就可以很轻松的打开一个网站,读取并打印信息。

    picture2

        urlopen有一些可选参数,具体信息可以查阅Python自带的documentation。

        了解到这些,我们就可以写一个最简单的程序,文件名为urllib_test01.py,感受一个urllib库的魅力:

    # -*- coding: UTF-8 -*-
    from urllib import request
    
    if __name__ == "__main__":
        response = request.urlopen("http://fanyi.baidu.com")
        html = response.read()
        print(html)

        urllib使用使用request.urlopen()打开和读取URLs信息,返回的对象response如同一个文本对象,我们可以调用read(),进行读取。再通过print(),将读到的信息打印出来。

        运行程序ctrl+b,可以在Sublime中查看运行结果,如下:

    picture3

        也可以在cmd(控制台)中输入指令:

    python urllib_test01.py

        运行py文件,输出信息是一样的,如下:

    picture5

        其实这就是浏览器接收到的信息,只不过我们在使用浏览器的时候,浏览器已经将这些信息转化成了界面信息供我们浏览。当然这些代码我们也可以从浏览器中查看到。例如,使用谷歌浏览器,在任意界面单击右键选择检查,也就是审查元素(不是所有页面都可以审查元素的,例如起点中文网付费章节就不行.),以百度界面为例,截图如下:

    picture6

        可以看到,右边就是我们的审查结果。我们可以在本地,也就是浏览器(客户端)更改元素,但是这个不会上传到服务器端。例如我可以修改自己的支付宝余额装一装,比如这样:

    picture7

        我实际有这些钱吗?显然苦逼的我,是不会有这些钱的,我只不过是修改了下审查元素的信息而已。

        有些跑偏,不过说的道理就是,浏览器就是作为客户端从服务器端获取信息,然后将信息解析,再展示给我们的。

        回归正题,虽然我们已经成功获取了信息,但是显然他们都是二进制的乱码,看起来很不方便。我们怎么办呢?

        我们可以通过简单的decode()命令将网页的信息进行解码,并显示出来,我们新创建一个文件,命名为urllib_test02.py,编写如下代码(还是以百度翻译网站fanyi.baidu.com为例):

    # -*- coding: UTF-8 -*-
    from urllib import request
    
    if __name__ == "__main__":
        response = request.urlopen("http://www.fanyi.baidu.com/")
        html = response.read()
        html = html.decode("utf-8")
        print(html)

        这样我们就可以得到这样的结果,显然解码后的信息看起来工整和舒服多了:

    picture8

        当然这个前提是我们已经知道了这个网页是使用utf-8编码的,怎么查看网页的编码方式呢?需要人为操作,且非常简单的方法是使用使用浏览器审查元素,只需要找到head标签开始位置的chareset,就知道网页是采用何种编码的了。如下:

    picture9

        这样我们就知道了这个网站的编码方式,但是这需要我们每次都打开浏览器,并找下编码方式,显然有些费事,使用几行代码解决更加省事并且显得酷一些。

    四、自动获取网页编码方式的方法

        获取网页编码的方式有很多,个人更喜欢用第三方库的方式。

        首先我们需要安装第三方库chardet,它是用来判断编码的模块,安装方法如下图所示,只需要输入指令:

    pip install chardet

    picture10

        安装好后,我们就可以使用chardet.detect()方法,判断网页的编码方式了。至此,我们就可以编写一个小程序判断网页的编码方式了,新建文件名为chardet_test01.py:

    # -*- coding: UTF-8 -*-
    from urllib import request
    import chardet
    
    if __name__ == "__main__":
        response = request.urlopen("http://fanyi.baidu.com/")
        html = response.read()
        charset = chardet.detect(html)
        print(charset)

        运行程序,查看输出结果如下:

    picture11

        瞧,返回的是一个字典,这样我们就知道网页的编码方式了,通过获得的信息,采用不同的解码方式即可。

    PS:关于编码方式的内容,可以自行百度,或者看下这篇博客:

    URL:http://blog.csdn.net/csywwx2008/article/details/17137097

    展开全文
  • 网页爬虫教程

    千次阅读 2018-09-08 00:18:54
    学习爬虫, 首先要懂的是网页. 支撑起各种光鲜亮丽的网页的不是别的, 全都是一些代码. 这种代码我们称之为 HTML, HTML 是一种浏览器(Chrome, Safari, IE, Firefox等)看得懂的语言, 浏览器能将这种语言转换成我...

    转自https://morvanzhou.github.io/tutorials/data-manipulation/scraping/

    了解网页结构

    学习爬虫, 首先要懂的是网页. 支撑起各种光鲜亮丽的网页的不是别的, 全都是一些代码. 这种代码我们称之为 HTML, HTML 是一种浏览器(Chrome, Safari, IE, Firefox等)看得懂的语言, 浏览器能将这种语言转换成我们用肉眼看到的网页. 所以 HTML 里面必定存在着很多规律, 我们的爬虫就能按照这样的规律来爬取你需要的信息.

    其实除了 HTML, 一同构建多彩/多功能网页的组件还有 CSS 和 JavaScript. 但是这个简单的爬虫教程, 大部分时间会将会使用 HTML. CSS 和 JavaScript 会在后期简单介绍一下. 因为爬网页的时候多多少少还是要和 CSS JavaScript 打交道的.
    这里写图片描述
    虽然莫烦Python主打的是机器学习的教程. 但是这个爬虫教程适用于任何想学爬虫的朋友们. 从机器学习的角度看, 机器学习中的大量数据, 也是可以从这些网页中来, 使用爬虫来爬取各种网页上面的信息, 然后再放入各种机器学习的方法, 这样的应用途径正在越来越多被采用. 所以如果你的数据也是分散在各个网页中, 爬虫是你减少人力劳动的必修课.

    网页基本组成部分

    在真正进入爬虫之前, 我们先来做一下热身运动, 弄明白网页的基础, HTML 有哪些组成部分, 是怎么样运作的. 如果你已经非常熟悉网页的构造了, 欢迎直接跳过这一节, 进入下面的学习.

    我制作了一个非常简易的网页, 给大家呈现以下最骨感的 HTML 结构. 如果你点开它, 呈现在你眼前的, 就是下面这张图的上半部分. 而下半部分就是我们网页背后的 HTML code.
    这里写图片描述
    想问我是如何看到 HTML 的 source code 的? 其实很简单, 在你的浏览器中 (我用的是 Google Chrome), 显示网页的地方, 点击鼠标右键, 大多数浏览器都会有类似这样一个选项 “View Page Source”. 点击它就能看到页面的源码了.

    这里写图片描述
    在 HTML 中, 基本上所有的实体内容, 都会有个 tag 来框住它. 而这个被 tag 住的内容, 就可以被展示成不同的形式, 或有不同的功能. 主体的 tag 分成两部分, header 和 body. 在 header 中, 存放这一些网页的网页的元信息, 比如说 title, 这些信息是不会被显示到你看到的网页中的. 这些信息大多数时候是给浏览器看, 或者是给搜索引擎的爬虫看.

    <head>
        <meta charset="UTF-8">
        <title>Scraping tutorial 1 | 莫烦Python</title>
        <link rel="icon" href="https://morvanzhou.github.io/static/img/description/tab_icon.png">
    </head>

    HTML 的第二大块是 body, 这个部分才是你看到的网页信息. 网页中的 heading, 视频, 图片和文字等都存放在这里. 这里的

    tag 就是主标题, 我们看到呈现出来的效果就是大一号的文字.

    里面的文字就是一个段落. 里面都是一些链接. 所以很多情况, 东西都是放在这些 tag 中的.
    <body>
        <h1>爬虫测试1</h1>
        <p>
            这是一个在 <a href="https://morvanzhou.github.io/">莫烦Python</a>
            <a href="https://morvanzhou.github.io/tutorials/scraping">爬虫教程</a> 中的简单测试.
        </p>
    </body>

    爬虫想要做的就是根据这些 tag 来找到合适的信息.

    用 Python 登录网页

    好了, 对网页结构和 HTML 有了一些基本认识以后, 我们就能用 Python 来爬取这个网页的一些基本信息. 首先要做的, 是使用 Python 来登录这个网页, 并打印出这个网页 HTML 的 source code. 注意, 因为网页中存在中文, 为了正常显示中文, read() 完以后, 我们要对读出来的文字进行转换, decode() 成可以正常显示中文的形式.

    from urllib.request import urlopen
    
    # if has Chinese, apply decode()
    html = urlopen(
        "https://morvanzhou.github.io/static/scraping/basic-structure.html"
    ).read().decode('utf-8')
    print(html)

    print 出来就是下面这样啦. 这就证明了我们能够成功读取这个网页的所有信息了. 但我们还没有对网页的信息进行汇总和利用. 我们发现, 想要提取一些形式的信息, 合理的利用 tag 的名字十分重要.

    <!DOCTYPE html>
    <html lang="cn">
    <head>
        <meta charset="UTF-8">
        <title>Scraping tutorial 1 | 莫烦Python</title>
        <link rel="icon" href="https://morvanzhou.github.io/static/img/description/tab_icon.png">
    </head>
    <body>
        <h1>爬虫测试1</h1>
        <p>
            这是一个在 <a href="https://morvanzhou.github.io/">莫烦Python</a>
            <a href="https://morvanzhou.github.io/tutorials/scraping">爬虫教程</a> 中的简单测试.
        </p>
    
    </body>
    </html>

    匹配网页内容

    所以这里我们使用 Python 的正则表达式 RegEx 进行匹配文字, 筛选信息的工作. 我有一个很不错的正则表达式的教程, 如果是初级的网页匹配, 我们使用正则完全就可以了, 高级一点或者比较繁琐的匹配, 我还是推荐使用 BeautifulSoup. 不急不急, 我知道你想偷懒, 我之后马上就会教 beautiful soup 了. 但是现在我们还是使用正则来做几个简单的例子, 让你熟悉一下套路.

    如果我们想用代码找到这个网页的 title, 我们就能这样写. 选好要使用的 tag 名称 . 使用正则匹配.

    import re
    res = re.findall(r"<title>(.+?)</title>", html)
    print("\nPage title is: ", res[0])
    
    # Page title is:  Scraping tutorial 1 | 莫烦Python

    如果想要找到中间的那个段落

    , 我们使用下面方法, 因为这个段落在 HTML 中还夹杂着 tab, new line, 所以我们给一个 flags=re.DOTALL 来对这些 tab, new line 不敏感.

    res = re.findall(r"<p>(.*?)</p>", html, flags=re.DOTALL)    # re.DOTALL if multi line
    print("\nPage paragraph is: ", res[0])
    
    # Page paragraph is:
    #  这是一个在 <a href="https://morvanzhou.github.io/">莫烦Python</a>
    #  <a href="https://morvanzhou.github.io/tutorials/scraping">爬虫教程</a> 中的简单测试.

    最后一个练习是找一找所有的链接, 这个比较有用, 有时候你想找到网页里的链接, 然后下载一些内容到电脑里, 就靠这样的途径了.

    res = re.findall(r'href="(.*?)"', html)
    print("\nAll links: ", res)
    # All links:
    ['https://morvanzhou.github.io/static/img/description/tab_icon.png',
    'https://morvanzhou.github.io/',
    'https://morvanzhou.github.io/tutorials/scraping']

    BeautifulSoup 解析网页: 基础

    我们了解了网页 (html) 的基本构架, 知道了爬网页就是在这个构架中找到需要的信息. 那么找到需要的信息时, BeautifulSoup 就是一个找信息好帮手. 它能帮你又快有准地找到信息. 大大简化了使用难度.
    我们总结一下爬网页的流程, 让你对 BeautifulSoup 有一个更好的定位.

    1. 选着要爬的网址 (url)
    2. 使用 python 登录上这个网址 (urlopen等)
    3. 读取网页信息 (read() 出来)
    4. 将读取的信息放入 BeautifulSoup
    5. 使用 BeautifulSoup 选取 tag 信息等 (代替正则表达式)
      初学的时候总是搞不懂这些包是干什么的, 现在你就能理解这个 BeautifulSoup 到底是干什么的了.

    安装

    等什么, 知道 BeautifulSoup 这么方便, 我们就赶紧装一个吧. 安装的步骤很简单, 用 pip 就能轻松安装.

    # Python 2+
    pip install beautifulsoup4
    
    # Python 3+
    pip3 install beautifulsoup4

    注意在名字后面还有个 “4”, 可能是代表第4版吧. 如果你在安装的时候遇到任何问题, 请参考他们官网上的解决方案.

    简单实用方法

    这次我们还是爬一爬上次爬的那个基本网页. BeautifulSoup 使用起来非常简单, 我们先按常规读取网页.

    from bs4 import BeautifulSoup
    from urllib.request import urlopen
    
    # if has Chinese, apply decode()
    html = urlopen("https://morvanzhou.github.io/static/scraping/basic-structure.html").read().decode('utf-8')
    print(html)

    回顾一下, 每张网页中, 都有两大块, 一个是 , 一个是 , 我们等会用 BeautifulSoup 来找到 body 中的段落

    和所有链接 .

    <!DOCTYPE html>
    <html lang="cn">
    <head>
        <meta charset="UTF-8">
        <title>Scraping tutorial 1 | 莫烦Python</title>
        <link rel="icon" href="https://morvanzhou.github.io/static/img/description/tab_icon.png">
    </head>
    <body>
        <h1>爬虫测试1</h1>
        <p>
            这是一个在 <a href="https://morvanzhou.github.io/">莫烦Python</a>
            <a href="https://morvanzhou.github.io/tutorials/scraping">爬虫教程</a> 中的简单测试.
        </p>
    
    </body>
    </html>

    读取这个网页信息, 我们将要加载进 BeautifulSoup, 以 lxml 的这种形式加载. 除了 lxml, 其实还有很多形式的解析器, 不过大家都推荐使用 lxml 的形式. 然后 soup 里面就有着这个 HTML 的所有信息. 如果你要输出

    标题, 可以就直接 soup.h1.

    soup = BeautifulSoup(html, features='lxml')
    print(soup.h1)
    
    """
    <h1>爬虫测试1</h1>
    """
    
    print('\n', soup.p)
    
    """
    <p>
            这是一个在 <a href="https://morvanzhou.github.io/">莫烦Python</a>
    <a href="https://morvanzhou.github.io/tutorials/scraping">爬虫教程</a> 中的简单测试.
        </p>
    """

    如果网页中有过个同样的 tag, 比如链接 , 我们可以使用 find_all() 来找到所有的选项. 因为我们真正的 link 不是在 中间 , 而是在 里面, 也可以看做是 的一个属性. 我们能用像 Python 字典的形式, 用 key 来读取 l[“href”].

    """
    <a href="https://morvanzhou.github.io/tutorials/scraping">爬虫教程</a>
    """
    
    all_href = soup.find_all('a')
    all_href = [l['href'] for l in all_href]
    print('\n', all_href)
    
    # ['https://morvanzhou.github.io/', 'https://morvanzhou.github.io/tutorials/scraping']

    懂得这些还是远远不够的, 真实情况往往比这些复杂. BeautifulSoup 还有很多其他的选择”增强器”. 下次, 我们来了解一些 CSS 的概念, 用 BeautifulSoup 加上 CSS 来选择内容.

    BeautifulSoup 解析网页: CSS

    BeautifulSoup 十分好用, 能快速定位到你需要的网页信息. 上次 我们学着使用了 BeautifulSoup, 这次我们将会了解它更强大的功能, 使用 CSS 的 Class 来选择内容.
    这里写图片描述

    什么是 CSS

    提到这个, 我们肯定需要知道什么是 CSS. HTML 和 CSS 是一对好搭档, 他们共同组成了当今的众多网页. 如果这个世界上没有 CSS, 你看到的所有网页可能都长得像这样. 特别”骨感”!
    这里写图片描述
    如果有 CSS, 你的网页就变得丰富多彩起来. 文字有了颜色, 字体, 位置也多样了, 图片也有规则了.
    这里写图片描述
    所以, CSS 主要用途就是装饰你 “骨感” HTML 页面. 如果将 HTML 比喻成没穿衣服的人, 那 CSS 就是五颜六色的衣服. 穿在人身上让人有了气质. CSS 的规则很多, 好在如果你只是需要爬网页, 你并不需要学习 CSS 的这些用法或规则,你只需要注意 CSS 的一条规则就能玩转爬虫了.

    CSS 的 Class

    这条规则就是 CSS 的 Class, CSS 在装饰每一个网页部件的时候, 都会给它一个名字. 而且一个类型的部件, 名字都可以一样. 比如我们这个练习网页. 里面的字体/背景颜色, 字体大小, 都是由 CSS 来掌控的.
    而 CSS 的代码, 可能就会放在这个网页的 中. 我们先使用 Python 读取这个页面.

    from bs4 import BeautifulSoup
    from urllib.request import urlopen
    
    # if has Chinese, apply decode()
    html = urlopen("https://morvanzhou.github.io/static/scraping/list.html").read().decode('utf-8')
    print(html)

    在 中, 你会发现有这样一些东西被放在

    <head>
        ...
        <style>
        .jan {
            background-color: yellow;
        }
        ...
        .month {
            color: red;
        }
        </style>
    </head>
    
    <body>
    ...
    <ul>
        <li class="month">一月</li>
        <ul class="jan">
            <li>一月一号</li>
            <li>一月二号</li>
            <li>一月三号</li>
        </ul>
        ...
    </ul>
    </body>

    这样, 我们就知道, 有时候, 网页中, 这种 class 归类一些组件还是很有用的. 比如我就想找 jan 下面的这些

  • . 我就能通过寻找 class=”jan” 找到它们. BeautifulSoup 就能这么干.
  • 按 Class 匹配

    按 Class 匹配很简单. 比如我要找所有 class=month 的信息. 并打印出它们的 tag 内文字.

    soup = BeautifulSoup(html, features='lxml')
    
    # use class to narrow search
    month = soup.find_all('li', {"class": "month"})
    for m in month:
        print(m.get_text())
    
    """
    一月
    二月
    三月
    四月
    五月
    """

    或者找到 class=jan 的信息. 然后在

      下面继续找
        内部的
      • 信息. 这样一层层嵌套的信息, 非常容易找到.

    jan = soup.find('ul', {"class": 'jan'})
    d_jan = jan.find_all('li')              # use jan as a parent
    for d in d_jan:
        print(d.get_text())
    
    """
    一月一号
    一月二号
    一月三号
    """

    如果想要找到一些有着一定格式的信息, 比如使用正则表达来寻找相类似的信息, 我们在 BeautifulSoup 中也能嵌入正则表达式, 让 BeautifulSoup 更为强大. 怎么用, 我们就接着往下看啦.

    BeautifulSoup 解析网页: 正则表达

    正则表达式, 是处理文本信息的重要工具, 除了 Python, 在其他的程序语言中, 也有十分重要的地位. 如果将正则表达式 + BeautifulSoup, 岂不是完美中的完美, 哈哈. 我们今天就来看看, 在 BeautifulSoup 中如何使用正则表达式, 获取更有难度的信息.
    这里写图片描述

    正则表达式

    正则表达式很厉害, 它能用简单的规则匹配到多样化的文本信息. 在做爬虫教程之前, 我特地做了一个正则表达式的教程, 为爬虫做铺垫. 所以有兴趣了解使用正则表达式的朋友, 都可以看看这个非常全的正则教程.
    这里写图片描述
    这次的教程有一些表格形式的 HTML, 在表格中, 有一些信息的格式类似, 我们先用 BeautifulSoup 筛选一些, 然后完全可以用正则给匹配出来. 比如你想下载这个页面的图片, 我们就可以将图片形式的 url 个匹配出来. 之后再下载就简单多了.

    正则匹配

    我们先读取这个网页. 导入正则模块 re.

    from bs4 import BeautifulSoup
    from urllib.request import urlopen
    import re
    
    # if has Chinese, apply decode()
    html = urlopen("https://morvanzhou.github.io/static/scraping/table.html").read().decode('utf-8')

    我们发现, 如果是图片, 它们都藏在这样一个 tag 中:

    <td>
        <img src="https://morvanzhou.github.io/static/img/course_cover/tf.jpg">
    </td>

    所以, 我们可以用 soup 将这些 tag 全部找出来, 但是每一个 img 的链接(src)都可能不同. 或者每一个图片有的可能是 jpg 有的是 png, 如果我们只想挑选 jpg 形式的图片, 我们就可以用这样一个正则 r’.*?.jpg’ 来选取. 把正则的 compile 形式放到 BeautifulSoup 的功能中, 就能选到符合要求的图片链接了.

    soup = BeautifulSoup(html, features='lxml')
    
    img_links = soup.find_all("img", {"src": re.compile('.*?\.jpg')})
    for link in img_links:
        print(link['src'])
    
    """
    https://morvanzhou.github.io/static/img/course_cover/tf.jpg
    https://morvanzhou.github.io/static/img/course_cover/rl.jpg
    https://morvanzhou.github.io/static/img/course_cover/scraping.jpg
    """

    所以, 我们可以用 soup 将这些 tag 全部找出来, 但是每一个 img 的链接(src)都可能不同. 或者每一个图片有的可能是 jpg 有的是 png, 如果我们只想挑选 jpg 形式的图片, 我们就可以用这样一个正则 r’.*?.jpg’ 来选取. 把正则的 compile 形式放到 BeautifulSoup 的功能中, 就能选到符合要求的图片链接了.

    soup = BeautifulSoup(html, features='lxml')
    
    img_links = soup.find_all("img", {"src": re.compile('.*?\.jpg')})
    for link in img_links:
        print(link['src'])
    
    """
    https://morvanzhou.github.io/static/img/course_cover/tf.jpg
    https://morvanzhou.github.io/static/img/course_cover/rl.jpg
    https://morvanzhou.github.io/static/img/course_cover/scraping.jpg
    """

    又或者我们发现, 我想选一些课程的链接, 而这些链接都有统一的形式, 就是开头都会有 https://morvan., 那我就将这个定为一个正则的规则, 让 BeautifulSoup 帮我找到符合这个规则的链接.

    course_links = soup.find_all('a', {'href': re.compile('https://morvan.*')})
    for link in course_links:
        print(link['href'])
    
    """
    https://morvanzhou.github.io/
    https://morvanzhou.github.io/tutorials/scraping
    https://morvanzhou.github.io/tutorials/machine-learning/tensorflow/
    https://morvanzhou.github.io/tutorials/machine-learning/reinforcement-learning/
    https://morvanzhou.github.io/tutorials/data-manipulation/scraping/
    """

    小练习: 爬百度百科

    有了前面几节内容了练习, 我们现在完全有能力爬出你想要的信息了. 看吧, 我说很简单的, 只要你用 Python 打开网页, 用 BeautifulSoup 找准地方, 然后这样循环往复, 就叫做爬虫了. 哈哈. 被我抽象得不行了. 不过说到底, 爬虫就这么回事. 今天我们就来爬一爬百度百科, 让我们的爬虫从 “网络爬虫” 这一页开始爬, 然后在页面中寻找其他页面的信息, 然后爬去其他页面, 然后循环这么做, 看看最后我们的爬虫到底爬去了哪.

    百度百科

    百度百科中有很多名词的解释信息, 我们今天从 “网页爬虫” 的词条开始爬, 然后在页面中任意寻找下一个词条, 爬过去, 再寻找词条, 继续爬. 看看最后我们爬到的词条和 “网页爬虫” 差别有多大.
    这里写图片描述
    这个练习看起来挺没意义的, 但是对于了解爬虫, 还是挺有意义的. 用最简单的规律解释了爬虫的真谛.

    观看规律

    这个爬虫说实在的, 并不难, 只有20+行代码. 但是却能让它游走在百度百科的知识的海洋中. 首先我们需要定义一个起始网页, 我选择了 “网页爬虫”. 我们发现, 页面中有一些链接, 指向百度百科中的另外一些词条, 比如说下面这样.

    <a target="_blank" href="/item/%E8%9C%98%E8%9B%9B/8135707" data-lemmaid="8135707">蜘蛛</a>
    <a target="_blank" href="/item/%E8%A0%95%E8%99%AB">蠕虫</a>
    <a target="_blank" href="/item/%E9%80%9A%E7%94%A8%E6%90%9C%E7%B4%A2%E5%BC%95%E6%93%8E">通用搜索引擎</a>

    通过观察, 我们发现, 链接有些共通之处. 它们都是 /item/ 开头, 夹杂着一些 %E9 这样的东西. 但是仔细搜索一下, 发现还有一些以 /item/ 开头的, 却不是词条. 比如

    <a href="/item/史记·2016?fr=navbar" target="_blank">史记·2016</a>

    我想, 我们需要对这些链接做一些筛选, 之前提到 的用 BeautifulSoup 和 正则表达式来筛选应该用得上. 有了些思路, 我们开始写代码吧.

    制作爬虫

    导入一些模块, 设置起始页. 并将 /item/… 的网页都放在 his 中, 做一个备案, 记录我们浏览过的网页.

    from bs4 import BeautifulSoup
    from urllib.request import urlopen
    import re
    import random
    
    
    base_url = "https://baike.baidu.com"
    his = ["/item/%E7%BD%91%E7%BB%9C%E7%88%AC%E8%99%AB/5162711"]

    接着我们先不用循环, 对一个网页进行处理, 走一遍流程, 然后加上循环, 让我们的爬虫能在很多网页中爬取. 下面做的事情, 是为了在屏幕上打印出来我们现在正在哪张网页上, 网页的名字叫什么.

    url = base_url + his[-1]
    
    html = urlopen(url).read().decode('utf-8')
    soup = BeautifulSoup(html, features='lxml')
    print(soup.find('h1').get_text(), '    url: ', his[-1])
    
    # 网络爬虫     url:  /item/%E7%BD%91%E7%BB%9C%E7%88%AC%E8%99%AB/5162711

    接下来我们开始在这个网页上找所有符合要求的 /item/ 网址. 使用一个正则表达式过滤掉不想要的网址形式. 这样我们找到的网址都是 /item/%xx%xx%xx… 这样的格式了. 之后我们在这些过滤后的网页中随机选一个, 当做下一个要爬的网页. 不过有时候很不幸, 在 sub_urls 中并不能找到合适的网页, 我们就往回跳一个网页, 回到之前的网页中再随机抽一个网页做同样的事.

    # find valid urls
    sub_urls = soup.find_all("a", {"target": "_blank", "href": re.compile("/item/(%.{2})+$")})
    
    if len(sub_urls) != 0:
        his.append(random.sample(sub_urls, 1)[0]['href'])
    else:
        # no valid sub link found
        his.pop()
    print(his)
    
    # ['/item/%E7%BD%91%E7%BB%9C%E7%88%AC%E8%99%AB/5162711', '/item/%E4%B8%8B%E8%BD%BD%E8%80%85']

    有了这套体系, 我们就能把它放在一个 for loop 中, 让它在各种不同的网页中跳来跳去.

    his = ["/item/%E7%BD%91%E7%BB%9C%E7%88%AC%E8%99%AB/5162711"]
    
    for i in range(20):
        url = base_url + his[-1]
    
        html = urlopen(url).read().decode('utf-8')
        soup = BeautifulSoup(html, features='lxml')
        print(i, soup.find('h1').get_text(), '    url: ', his[-1])
    
        # find valid urls
        sub_urls = soup.find_all("a", {"target": "_blank", "href": re.compile("/item/(%.{2})+$")})
    
        if len(sub_urls) != 0:
            his.append(random.sample(sub_urls, 1)[0]['href'])
        else:
            # no valid sub link found
            his.pop()

    这样我们就能观看我们的爬虫现在爬去了哪? 是不是爬到了和 “网页爬虫” 起始页完全不相关的地方去了.

    0 网络爬虫     url:  /item/%E7%BD%91%E7%BB%9C%E7%88%AC%E8%99%AB/5162711
    1 路由器     url:  /item/%E8%B7%AF%E7%94%B1%E5%99%A8
    2 服务等级     url:  /item/%E6%9C%8D%E5%8A%A1%E7%AD%89%E7%BA%A7
    ...
    17 呼损率     url:  /item/%E5%91%BC%E6%8D%9F%E7%8E%87
    18 服务等级     url:  /item/%E6%9C%8D%E5%8A%A1%E7%AD%89%E7%BA%A7
    19 呼损率     url:  /item/%E5%91%BC%E6%8D%9F%E7%8E%87

    多功能的 Requests

    之前 我们通常使用 Python 的自带模块 urllib, 来提交网页请求. 这个模块能满足我们大部分的需求, 但是为了满足你日益膨胀的其他需求, 比如向网页发送信息, 上传图片等等, 我们还有一个伟大的 Python 外部模块 requests, 来有效的处理这些问题.
    这里写图片描述

    获取网页的方式

    其实在加载网页的时候, 有几种类型, 而这几种类型就是你打开网页的关键. 最重要的类型 (method) 就是 get 和 post (当然还有其他的, 比如 head, delete). 刚接触网页构架的朋友可能又会觉得有点懵逼了. 这些请求的方式到底有什么不同? 他们又有什么作用?

    我们就来说两个重要的, get, post, 95% 的时间, 你都是在使用这两个来请求一个网页.

    post
    账号登录
    搜索内容
    上传图片
    上传文件
    往服务器传数据 等
    get
    正常打开网页
    不往服务器传数据
    这样看来, 很多网页使用 get 就可以了, 比如 莫烦Python 里的所有页面, 都是只是 get 发送请求. 而 post, 我们则是给服务器发送个性化请求, 比如将你的账号密码传给服务器, 让它给你返回一个含有你个人信息的 HTML.

    从主动和被动的角度来说, post 中文是发送, 比较主动, 你控制了服务器返回的内容. 而 get 中文是取得, 是被动的, 你没有发送给服务器个性化的信息, 它不会根据你个性化的信息返回不一样的 HTML.
    拿登录账号举个例子. 比如我想登录知乎账号(https://www.zhihu.com/people/morvan/activities, 在登录前, 我看到的页面是这样.
    这里写图片描述
    而如果我登录了(登录一般用 post), 我再次登录这个网页 https://www.zhihu.com/people/morvan/activities, 现在显示的 HTML 界面就和我上面那个不一样了, 这就是使用 post 给网页输入你的个性化信息后 (账号密码), 得到的服务器返回的个性化网页. 每个人的账号密码的到的页面都不一样.
    这里写图片描述
    而今天要说的 requests 模块就是干这些的. 它有着各种不同的请求方法, 而且用起来很方便.

    安装 requests

    Requests 是一个 Python 的外部模块, 我们需要手动安装它. 简单的方法, 在你的 terminal 或者是 cmd, 使用 pip 安装就好了.

    # python 2+
    pip install requests
    
    # python 3+
    pip3 install requests

    requests get 请求

    有了 requests, 我们可以发送个中 method 的请求. 比如 get. 我们想模拟一下百度的搜索. 首先我们需要观看一下百度搜索的规律. 在百度搜索框中写上 “莫烦python” 我们发现它弹出了一串这么长的网址.
    这里写图片描述
    但是仔细一看, 和 “莫烦Python” 有关的信息, 只有前面一小段 (“s?wd=莫烦python”), 其他的对我们来说都是无用的信息. 所以我们现在来尝试一下如果把后面的”无用” url 都去掉会怎样? Duang! 我们还是能搜到 “莫烦python”.
    这里写图片描述
    所以 “s?wd=莫烦python” 这就是我们搜索需要的关键信息. 我们就能用 get 来搭配一些自定义的搜索关键词来用 python 个性化搜索. 首先, 我们固定不动的网址部分是 “http://www.baidu.com/s”, ? 后面的东西都是一些参数 (parameters), 所以我们将这些 parameters 用 python 的字典代替, 然后传入 requests.get() 功能. 然后我们还能用 python (webbrowser模块) 打开一个你的默认浏览器, 观看你是否在百度的搜索页面.

    import requests
    import webbrowser
    param = {"wd": "莫烦Python"}  # 搜索的信息
    r = requests.get('http://www.baidu.com/s', params=param)
    print(r.url)
    webbrowser.open(r.url)
    
    # http://www.baidu.com/s?wd=%E8%8E%AB%E7%83%A6Python

    这时, python 会弹出一个浏览器界面, 然后你看到的, 就是 “莫烦Python” 的搜索结果了.

    requests post 请求

    post 又怎么用呢? 我们举个小例子, 在这个简单网页中, 我们有一个提交信息的窗口, 如果我提交上去这个信息, 那边的服务器会更加这个提交的信息返回出另一个网页. 这就是网页怎么样使用你 post 过去的信息了.
    这里写图片描述
    比如我在这里填上自己的姓名, 当我点 “submit” 的时候, 这个姓名(Morvan, Zhou) 就会被提交给服务器, 然后它会根据提交的姓名返回这个网页.
    这里写图片描述
    这样咋看起来好像和上面讲的 get 百度搜索没区别呀? 都是提交一些信息, 返回一个界面. 但是, 重点来了. 你看看网址栏. 你 post 上去的个人信息, 有没有显示在 url 里? 你愿意将你的私密信息显示在 url 里吗? 你 post 过去的信息是交给服务器内部处理的. 不是用来显示在网址上的.
    懂了这些, 我们就来看使用 python 和 requests 怎么做 post 这个操作吧.

    首先我们调出浏览器的 inspect (右键点击 inspect, 中文是检查还是什么来着). 然后发现我们填入姓名的地方原来是在一个 里面.
    这里写图片描述
    这个 里面有一些 个 tag, 我们仔细看到 里面的这个值 name=”firstname” 和 name=”lastname”, 这两个就是我们要 post 提交上去的关键信息了. 我们填好姓名, 为了记录点击 “submit” 后, 浏览器究竟发生了什么翻天覆地的变化, 我们在 inspect 窗口, 选择 Network, 勾选 Preserve log, 再点击 “submit”, 你就能看到服务器返回给你定制化后的页面时, 你使用的方法和数据.
    这里写图片描述
    这些数据包括了:

    Request URL (post 要用的 URL)
    Request Method (post)
    Form Data (post 去的信息)
    有了这些记录, 我们就能开始写 Python 来模拟这一次提交 post 了. 根据 ‘firstname’ 和 ‘lastname’, 也就是上图里面的 Form data, 组织成一个 python 字典. 让后把这个字典传入 requests.post(), 注意, 这里的 post 里面的 url, 不是我们填表时的 url (http://pythonscraping.com/pages/files/form.html), 而是要把 Form 信息提交去的那个网页, 也就是上图中查看到的 Request URL (http://pythonscraping.com/files/processing.php).

    data = {'firstname': '莫烦', 'lastname': '周'}
    r = requests.post('http://pythonscraping.com/files/processing.php', data=data)
    print(r.text)
    
    # Hello there, 莫烦 周!

    通过这个练习, 我们对 HTML 中的 Form 有了理解, 学会了怎么样使用 python 来提交 Form, 登录上提交后的页面.

    上传图片

    传照片也是 post 的一种, 我们得将本地的照片文件传送到服务器. 我们使用这个网页来模拟一次传照片的过程.
    这里写图片描述
    如果你留意观察 url, 你会发现, 传送完照片以后的 url 有变动. 我们使用同样的步骤再次检查, 发现, “choose file” 按键链接的 是一个叫 uploadFile 的名字. 我们将这个名字记下, 放入 python 的字典当一个 “key”.
    这里写图片描述
    接着在字典中, 使用 open 打开一个图片文件, 当做要上传的文件. 把这个字典放入你的 post 里面的 files 参数. 就能上传你的图片了, 网页会返回一个页面, 将你的图片名显示在上面.

    file = {'uploadFile': open('./image.png', 'rb')}
    r = requests.post('http://pythonscraping.com/files/processing2.php', files=file)
    print(r.text)
    
    # The file image.png has been uploaded.

    登录

    用 post 还有一个重要的, 就是模拟登录. 再登录的时候发生了什么事情呢? 我们使用这个简单的登录网页进行说明.
    这里写图片描述
    通过之前提到的方法, 我们观察一下浏览器给出的记录. 三个重要的方面都被我圈出来了.
    这里写图片描述
    我们总结一下, 为了这次登录账号, 我们的浏览器做了什么.

    1. 使用 post 方法登录了第一个红框的 url
    2. post 的时候, 使用了 Form data 中的用户名和密码
    3. 生成了一些 cookies
      第三点我们是从来没有提到过的. cookie, 听起来很熟呀! 每当游览器出现问题的时候, 网上的解决方法是不是都有什么清除 cookie 之类的, 那 cookie 实际上是什么呢? 这里给出了和全面的介绍.

    简单来说, 因为打开网页时, 每一个页面都是不连续的, 没有关联的, cookies 就是用来衔接一个页面和另一个页面的关系. 比如说当我登录以后, 浏览器为了保存我的登录信息, 将这些信息存放在了 cookie 中. 然后我访问第二个页面的时候, 保存的 cookie 被调用, 服务器知道我之前做了什么, 浏览了些什么. 像你在网上看到的广告, 为什么都可能是你感兴趣的商品? 你登录淘宝, 给你推荐的为什么都和你买过的类似? 都是 cookies 的功劳, 让服务器知道你的个性化需求.

    所以大部分时候, 每次你登录, 你就会有一个 cookies, 里面会提到你已经是登录状态了. 所以 cookie 在这时候很重要. cookies 的传递也特别重要, 比如我用 requests.post + payload 的用户信息发给网页, 返回的 r 里面会有生成的 cookies 信息. 接着我请求去登录后的页面时, 使用 request.get, 并将之前的 cookies 传入到 get 请求. 这样就能已登录的名义访问 get 的页面了.

    payload = {'username': 'Morvan', 'password': 'password'}
    r = requests.post('http://pythonscraping.com/pages/cookies/welcome.php', data=payload)
    print(r.cookies.get_dict())
    
    # {'username': 'Morvan', 'loggedin': '1'}
    
    
    r = requests.get('http://pythonscraping.com/pages/cookies/profile.php', cookies=r.cookies)
    print(r.text)
    
    # Hey Morvan! Looks like you're still logged into the site!

    这里写图片描述

    使用 Session 登录

    不过每次都要传递 cookies 是很麻烦的, 好在 requests 有个很 handy 的功能, 那就是 Session. 在一次会话中, 我们的 cookies 信息都是相连通的, 它自动帮我们传递这些 cookies 信息. 这时我感叹, 程序员真会偷懒~ 哈哈.

    同样是执行上面的登录操作, 下面就是使用 session 的版本. 创建完一个 session 过后, 我们直接只用 session 来 post 和 get. 而且这次 get 的时候, 我们并没有传入 cookies. 但是实际上 session 内部就已经有了之前的 cookies 了.

    session = requests.Session()
    payload = {'username': 'Morvan', 'password': 'password'}
    r = session.post('http://pythonscraping.com/pages/cookies/welcome.php', data=payload)
    print(r.cookies.get_dict())
    
    # {'username': 'Morvan', 'loggedin': '1'}
    
    
    r = session.get("http://pythonscraping.com/pages/cookies/profile.php")
    print(r.text)
    
    # Hey Morvan! Looks like you're still logged into the site!

    下载文件

    从网上下载文件的时候你肯定很开心, 比如说什么电影, pdf, 音乐等. 我们使用爬虫, python 一样也可以做到. 而且做得途径还有很多. 今天我们就来探讨有哪些可行的途径, 之后我们还能用这些途径来做一个小实战, 下载国家地理杂志上的美图.

    下载之前

    在下载之前, 我们的弄清楚怎么样下载. 打个比方, 以前有朋友留言说我的教程太多, 不知道从何学起, 我专门制作了一张学习流程图, 这张莫烦Python的个性化学习路线应该也拯救了无数迷途少年吧. 哈哈哈, 今天我们来爬这张图, 还有下载这张图.
    这里写图片描述
    想下这张图, 我们首先要到这张图所在的网页. 在这个网页中找到这张图的位置, 并右键 inspect, 找到它在 HTML 中的信息.
    这里写图片描述
    发现原图被存放在这个网页, 注意这个地址开头是 /, 并不是完整的网址, 这种形式代表着, 它是在 “https://morvanzhou.github.io/” 下面的网址. 所以我们还要将其补全, 才能在网址栏中找到这个图片地址.

    src="/static/img/description/learning_step_flowchart.png"

    补全后的网址是:

    https://morvanzhou.github.io/static/img/description/learning_step_flowchart.png

    找到了这个网址, 我们就能开始下载了. 为了下载到一个特定的文件夹, 我们先建立一个文件夹吧. 并且规定这个图片下载地址.

    import os
    os.makedirs('./img/', exist_ok=True)
    
    IMAGE_URL = "https://morvanzhou.github.io/static/img/description/learning_step_flowchart.png"

    使用 urlretrieve

    在 urllib 模块中, 提供了我们一个下载功能 urlretrieve. 使用起来很简单. 输入下载地址 IMAGE_URL 和要存放的位置. 图片就会被自动下载过去了.

    from urllib.request import urlretrieve
    urlretrieve(IMAGE_URL, './img/image1.png')

    使用 request

    而在 requests 模块, 也能拿来下东西. 下面的代码实现了和上面一样的功能, 但是稍微长了点. 但我们为什么要提到 requests 的下载呢? 因为使用它的另一种方法, 我们可以更加有效率的下载大文件.

    import requests
    r = requests.get(IMAGE_URL)
    with open('./img/image2.png', 'wb') as f:
        f.write(r.content)

    所以说, 如果你要下载的是大文件, 比如视频等. requests 能让你下一点, 保存一点, 而不是要全部下载完才能保存去另外的地方. 这就是一个 chunk 一个 chunk 的下载. 使用 r.iter_content(chunk_size) 来控制每个 chunk 的大小, 然后在文件中写入这个 chunk 大小的数据.

    r = requests.get(IMAGE_URL, stream=True)    # stream loading
    
    with open('./img/image3.png', 'wb') as f:
        for chunk in r.iter_content(chunk_size=32):
            f.write(chunk)

    小练习: 下载美图

    学会爬虫, 关键在于练习, 见多识广, 见多了网页的构造, 才知道怎么爬. 今天我们就来一个小实战, 结合之前学习的 requests 访问和 下载功能, 还有 BeautifulSoup, 来下载一些国家地理杂志的美图.
    这里写图片描述

    找到图片位置

    说白了, 每次的爬虫, 都是先分析一下这个网页要找的东西的位置, 然后怎么索引上这个位置, 最后用 python 找到它. 这次也是这个逻辑. 我们看看今天要爬的这个图片网址. 定位到最新图片的位置,
    这里写图片描述
    找到这张图片的所在位置, 对比这类型的图片, 找到一种手段来筛选这些图片. 发现他们都存在于 img_list 的这种

      中.
      这里写图片描述
      而图片地址都是在 中.

    <img src="http://image.nationalgeographic.com.cn/2017/1228/20171228030617696.jpg">

    现在我们有了思路, 先找带有 img_list 的这种

      , 然后在
        里面找 .

    下载图片

    有了思路, 现在我们就用 python 来下图吧. import BeautifulSoup 和 requests. 定义爬取的 url.

    from bs4 import BeautifulSoup
    import requests
    
    URL = "http://www.nationalgeographic.com.cn/animals/"

    用 BeautifulSoup 找到带有 img_list 的这种

      ,

    html = requests.get(URL).text
    soup = BeautifulSoup(html, 'lxml')
    img_ul = soup.find_all('ul', {"class": "img_list"})

    从 ul 中找到所有的 , 然后提取 的 src 属性, 里面的就是图片的网址啦. 接着, 就用之前在 requests 下载那节内容里提到的一段段下载.

    for ul in img_ul:
        imgs = ul.find_all('img')
        for img in imgs:
            url = img['src']
            r = requests.get(url, stream=True)
            image_name = url.split('/')[-1]
            with open('./img/%s' % image_name, 'wb') as f:
                for chunk in r.iter_content(chunk_size=128):
                    f.write(chunk)
            print('Saved %s' % image_name)
    
    """
    Saved 20171227102206573.jpg
    ...
    Saved 20171214020322682.jpg
    """

    我从下载好的照片中抽了一张出来, 哈哈, 是张河马.
    这里写图片描述

    加速爬虫: 多进程分布式

    当你看到这里的时候, 说明你已经不满足于自己的爬虫速度, 你想要最求更快, 更便捷的爬虫方法. 你常常会听到用爬虫的人说分布式爬虫. 这就是为了体现便捷和效率而出现的方法. 这一节内容, 我们简单地介绍一下我使用的分布式爬虫方法, 并且用 python 的 multiprocessing 模块编写一个分布式爬虫.

    什么是分布式爬虫

    分布式爬虫主要是为了非常有效率的抓取网页, 我们的程序一般是单线程跑的, 指令也是一条条处理的, 每执行完一条指令才能跳到下一条. 那么在爬虫的世界里, 这里存在着一个问题.

    如果你已经顺利地执行过了前几节的爬虫代码, 你会发现, 有时候代码运行的时间大部分都花在了下载网页上. 有时候不到一秒能下载好一张网页的 HTML, 有时候却要几十秒. 而且非要等到 HTML 下载好了以后, 才能执行网页分析等步骤. 这非常浪费时间.

    如果我们能合理利用计算资源, 在下载一部分网页的时候就已经开始分析另一部分网页了. 这将会大大节省整个程序的运行时间. 又或者, 我们能同时下载多个网页, 同时分析多个网页, 这样就有种事倍功半的效用. 分布式爬虫的体系有很多种, 处理优化的问题也是多样的. 这里有一篇博客可以当做扩展阅读, 来了解当今比较流行的分布式爬虫框架.

    我们的分布式爬虫

    而今天我们想搭建的这一个爬虫, 就是同时下载, 同时分析的这一种类型的分布式爬虫. 虽然算不上特别优化的框架, 但是概念理解起来比较容易. 我有尝试过徒手写高级一点的分布式爬虫, 但是写起来非常麻烦. 我琢磨了一下, 打算给大家介绍的这种分布式爬虫代码也较好写, 而且效率比普通爬虫快了3.5倍. 我也特地画了张图给大家解释一下要搭建的分布式爬虫.
    这里写图片描述
    主要来说, 我们最开始有一个网页, 比如说是莫烦Python的首页, 然后首页中有很多 url, 我们使用多进程 (Python多进程教程) 同时开始下载这些 url, 得到这些 url 的 HTML 以后, 同时开始解析 (比如 BeautifulSoup) 网页内容. 在网页中寻找这个网站还没有爬过的链接. 最终爬完整个 莫烦 Python 网站所有页面.

    有了这种思路, 我们就可以开始写代码了. 你可以在我的 Github 一次性观看全部代码.

    首先 import 全部要用的模块, 并规定一个主页. 注意, 我用这份代码测试我内网的网站(速度不受外网影响) 所以使用的 base_url 是 “http://127.0.0.1:4000/”, 如果你要爬 莫烦Python, 你的 base_url 要是 “https://morvanzhou.github.io/” (下载速度会受外网影响).

    import multiprocessing as mp
    import time
    from urllib.request import urlopen, urljoin
    from bs4 import BeautifulSoup
    import re
    
    # base_url = "http://127.0.0.1:4000/"
    base_url = 'https://morvanzhou.github.io/'

    我们定义两个功能, 一个是用来爬取网页的(crawl), 一个是解析网页的(parse). 有了前几节内容的铺垫, 你应该能一言看懂下面的代码. crawl() 用 urlopen 来打开网页, 我用的内网测试, 所以为了体现下载网页的延迟, 添加了一个 time.sleep(0.1) 的下载延迟. 返回原始的 HTML 页面, parse() 就是在这个 HTML 页面中找到需要的信息, 我们用 BeautifulSoup 找 (BeautifulSoup 教程). 返回找到的信息.

    def crawl(url):
        response = urlopen(url)
        # time.sleep(0.1)             # slightly delay for downloading
        return response.read().decode()
    
    
    def parse(html):
        soup = BeautifulSoup(html, 'lxml')
        urls = soup.find_all('a', {"href": re.compile('^/.+?/$')})
        title = soup.find('h1').get_text().strip()
        page_urls = set([urljoin(base_url, url['href']) for url in urls])   # 去重
        url = soup.find('meta', {'property': "og:url"})['content']
        return title, page_urls, url

    网页中爬取中, 肯定会爬到重复的网址, 为了去除掉这些重复, 我们使用 python 的 set 功能. 定义两个 set, 用来搜集爬过的网页和没爬过的.

    unseen = set([base_url,])
    seen = set()

    测试普通爬法

    为了对比效果, 我们将在下面对比普通的爬虫和这种分布式的效果. 如果是普通爬虫, 我简化了一下接下来的代码, 将一些不影响的代码去除掉了, 如果你想看全部的代码, 请来到我的 Github. 我们用循环一个个 crawl unseen 里面的 url, 爬出来的 HTML 放到 parse 里面去分析得到结果. 接着就是更新 seen 和 unseen 这两个集合了.

    特别注意: 任何网站都是有一个服务器压力的, 如果你爬的过于频繁, 特别是使用多进程爬取或异步爬取, 一次性提交请求给服务器太多次, 这将可能会使得服务器瘫痪, 你可能再也看不到莫烦 Python 了. 所以为了安全起见, 我限制了爬取数量(restricted_crawl=True). 因为我测试使用的是内网 “http://127.0.0.1:4000/” 所以不会有这种压力. 你在以后的爬网页中, 会经常遇到这样的爬取次数的限制 (甚至被封号). 我以前爬 github 时就被限制成一小时只能爬60页.

    # DON'T OVER CRAWL THE WEBSITE OR YOU MAY NEVER VISIT AGAIN
    if base_url != "http://127.0.0.1:4000/":
        restricted_crawl = True
    else:
        restricted_crawl = False
    
    while len(unseen) != 0:                 # still get some url to visit
        if restricted_crawl and len(seen) >= 20:
            break
        htmls = [crawl(url) for url in unseen]
        results = [parse(html) for html in htmls]
    
        seen.update(unseen)         # seen the crawled
        unseen.clear()              # nothing unseen
    
        for title, page_urls, url in results:
            unseen.update(page_urls - seen)     # get new url to crawl

    使用这种单线程的方法, 在我的内网上面爬, 爬完整个 莫烦Python, 一共消耗 52.3秒. 接着我们把它改成多进程分布式.

    测试分布式爬法

    还是上一个 while 循环, 首先我们创建一个进程池(Pool). 不太懂进程池的朋友看过来. 然后我们修改得到 htmls 和 results 的两句代码. 其他都不变, 只将这两个功能给并行了. 我在这里写的都是简化代码, 你可以在这里 看到完整代码.

    pool = mp.Pool(4)
    while len(unseen) != 0:
        # htmls = [crawl(url) for url in unseen]
        # --->
        crawl_jobs = [pool.apply_async(crawl, args=(url,)) for url in unseen]
        htmls = [j.get() for j in crawl_jobs]
    
        # results = [parse(html) for html in htmls]
        # --->
        parse_jobs = [pool.apply_async(parse, args=(html,)) for html in htmls]
        results = [j.get() for j in parse_jobs]
    
        ...

    还是在内网测试, 只用了 16.3秒!! 这可比上面的单线程爬虫快了3.5倍. 而且我还不是在外网测试的. 如果在外网, 爬取一张网页的时间更长, 使用多进程会更加有效率, 节省的时间更多.

    看到这里, 你一定觉得多线程是爬虫的救星. 其实不然, 要不然我们的教程为什么还能继续. 哈哈. 下一节, 我们会讲到比多进程更加厉害的一种方法. 叫做异步爬取 (asyncio 模块).

    加速爬虫: 异步加载 Asyncio

    之前我一直在想如何用 multiprocessing 或者 threading 加速我的爬虫, 也做过了一些小实验, 确实, 我们看到了不小的效率提升. 但是当我更加深入的时候, 我发现, Python 还提供了一个有力的工具, 叫做 asyncio. 这是一个仅仅使用单线程, 就能达到多线程/进程的效果的工具. 它的原理, 简单说就是: 在单线程里使用异步计算, 下载网页的时候和处理网页的时候是不连续的, 更有效利用了等待下载的这段时间.

    传统的单线程下载处理网页可能就像下图(来源)左边蓝色那样, 计算机执行一些代码, 然后等待下载网页, 下好以后, 再执行一些代码… 或者在等待的时候, 用另外一个线程执行其他的代码, 这是多线程的手段. 那么 asyncio 就像右边, 只使用一个线程, 但是将这些等待时间统统掐掉, 下载应该都调到了后台, 这个时间里, 执行其他异步的功能, 下载好了之后, 再调回来接着往下执行.
    这里写图片描述
    如果换一张 Python 自家解释 asyncio 的图(来源), 虽然稍微复杂一点, 但是就是和上图想要表达的是一个意思.
    这里写图片描述
    那么, 我们今天就来尝试使用 asyncio 来替换掉 multiprocessing 或者 threading, 看看效果如何.

    Asyncio 库

    Asyncio 库是 Python 的原装库, 但是是在 Python 3 的时候提出来的, Python 2 和 Python 3.3- 是没有的. 而且 Python 3.5 之后, 和 Python 3.4 前在语法上还是有些不同, 比如 “await” 和 “yield” 的使用, 下面的教程都是基于 Python 3.5+, 使用 Python3.4 的可能会执行有点问题. 调整一下就好.

    在 3.5+ 版本中, asyncio 有两样语法非常重要, async, await. 弄懂了它们是如何协同工作的, 我们就完全能发挥出这个库的功能了. 剧透一下, 等会使用单线程爬网页的 asyncio 和之前多进程写的爬网页效果差不多, 而且当并行的进程数少的时候, asyncio 效果还会比多进程快.

    基本用法

    接着我们来举例介绍 asyncio, 像之前画的图那样, 我们要时刻记住, asyncio 不是多进程, 也不是多线程, 单单是一个线程, 但是是在 Python 的功能间切换着执行. 切换的点用 await 来标记, 能够异步的功能用 async 标记, 比如 async def function():. 首先我们看一下, 不使用 async 完成的一份代码, 然后我们将这份代码改成 async 版的. 这些代码我都会放在我的 github 中, 如果想一次性看全部, 请来这里.

    # 不是异步的
    import time
    
    
    def job(t):
        print('Start job ', t)
        time.sleep(t)               # wait for "t" seconds
        print('Job ', t, ' takes ', t, ' s')
    
    
    def main():
        [job(t) for t in range(1, 3)]
    
    
    t1 = time.time()
    main()
    print("NO async total time : ", time.time() - t1)
    
    """
    Start job  1
    Job  1  takes  1  s
    Start job  2
    Job  2  takes  2  s
    NO async total time :  3.008603096008301
    """

    从上面可以看出, 我们的 job 是按顺序执行的, 必须执行完 job 1 才能开始执行 job 2, 而且 job 1 需要1秒的执行时间, 而 job 2 需要2秒. 所以总时间是 3 秒多. 而如果我们使用 asyncio 的形式, job 1 在等待 time.sleep(t) 结束的时候, 比如是等待一个网页的下载成功, 在这个地方是可以切换给 job 2, 让它开始执行.

    import asyncio
    
    
    async def job(t):                   # async 形式的功能
        print('Start job ', t)
        await asyncio.sleep(t)          # 等待 "t" 秒, 期间切换其他任务
        print('Job ', t, ' takes ', t, ' s')
    
    
    async def main(loop):                       # async 形式的功能
        tasks = [
        loop.create_task(job(t)) for t in range(1, 3)
        ]                                       # 创建任务, 但是不执行
        await asyncio.wait(tasks)               # 执行并等待所有任务完成
    
    t1 = time.time()
    loop = asyncio.get_event_loop()             # 建立 loop
    loop.run_until_complete(main(loop))         # 执行 loop
    loop.close()                                # 关闭 loop
    print("Async total time : ", time.time() - t1)
    
    """
    Start job  1
    Start job  2
    Job  1  takes  1  s
    Job  2  takes  2  s
    Async total time :  2.001495838165283
    """

    从结果可以看出, 我们没有等待 job 1 的结束才开始 job 2, 而是 job 1 触发了 await 的时候就切换到了 job 2 了. 这时, job 1 和 job 2 同时在等待 await asyncio.sleep(t), 所以最终的程序完成时间, 取决于等待最长的 t, 也就是 2秒. 这和上面用普通形式的代码相比(3秒), 的确快了很多.

    aiohttp

    有了对 asyncio 的基本了解, 我们就来看怎么把它用在爬虫. 这个功能对于爬虫非常的理想, 原因很简单, 我们在等待一个网页下载的时候, 完全可以切换到其它代码, 事半功倍. 但是 asycio 自己还是没办法完成这项任务的, 我们还需要安装另一个牛逼的模块将 requests 模块代替成一个异步的 requests, 这个牛逼的模块叫作 aiohttp (官网在这). 下载安装特别简单. 直接在你的 terminal 或者 cmd 里面输入 “pip3 install aiohttp”.

    接着我们来看看我们怎么用最一般的 requests 模块爬网页, 和我们怎么将 requests 替换成 aiohttp.

    import requests
    
    URL = 'https://morvanzhou.github.io/'
    
    
    def normal():
        for i in range(2):
            r = requests.get(URL)
            url = r.url
            print(url)
    
    t1 = time.time()
    normal()
    print("Normal total time:", time.time()-t1)
    
    """
    https://morvanzhou.github.io/
    https://morvanzhou.github.io/
    Normal total time: 0.3869960308074951
    """

    用 requests 用久了以后, 这样的代码真是信手拈来. 很好, 我们打开 莫烦 Python 的首页两次只花了 0.38秒. 然后我们在用 aiohttp 来实现一样的功能. 结果 asyncio 的方式只用了 0.11秒! 大获全胜.

    import aiohttp
    
    
    async def job(session):
        response = await session.get(URL)       # 等待并切换
        return str(response.url)
    
    
    async def main(loop):
        async with aiohttp.ClientSession() as session:      # 官网推荐建立 Session 的形式
            tasks = [loop.create_task(job(session)) for _ in range(2)]
            finished, unfinished = await asyncio.wait(tasks)
            all_results = [r.result() for r in finished]    # 获取所有结果
            print(all_results)
    
    t1 = time.time()
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main(loop))
    loop.close()
    print("Async total time:", time.time() - t1)
    
    """
    ['https://morvanzhou.github.io/', 'https://morvanzhou.github.io/']
    Async total time: 0.11447715759277344
    """
    

    我们刚刚创建了一个 Session, 这是官网推荐的方式, 但是我觉得也可以直接用 request 形式, 细节请参考官方说明. 如果要获取网页返回的结果, 我们可以在 job() 中 return 个结果出来, 然后再在 finished, unfinished = await asyncio.wait(tasks) 收集完成的结果, 这里它会返回完成的和没完成的, 我们关心的都是完成的, 而且 await 也确实是等待都完成了才返回. 真正的结果被存放在了 result() 里面.

    和多进程分布式爬虫对比

    有了这些基础, 我们就可以来玩点高级的了, 之前我们用 multiprocessing 写过了一个简单的分布式爬虫, 现在我们就来拿过来 PK 一下 asyncio 的方法. 首先我们对比一下这次写的结构和上次写的简单分布式爬虫的区别. 分布式我们完全依赖的是 multiprocessing 这个模块. 不了解的可以快速过一遍这个教程. 使用 python 强大的并行处理运算来下载我们要处理的 urls, 然后解析网页也是一件耗时的事, 特别是网页量多的时候. 所以我们也将网页解析给并行了. 这样大大节省了下载和运算时间. 再看右边的这个 asyncio 的例子, 我们解析网页还是用的和 multiprocessing 那边一样的并行处理, 因为 asyncio 好像不支持解析网页的异步, 毕竟是计算密集型工序. 然后不一样的地方是, 我们在下载网页时, 不用 multiprocessing, 改用 asyncio, 用一个单线程的东西挑战多进程.
    这里写图片描述
    特别注意: 任何网站都是有一个服务器压力的, 如果你爬的过于频繁, 特别是使用多进程爬取或异步爬取, 一次性提交请求给服务器太多次, 这将可能会使得服务器瘫痪, 你可能再也看不到莫烦 Python 了. 所以为了安全起见, 我限制了爬取数量(restricted_crawl=True). 因为我测试使用的是内网 “http://127.0.0.1:4000/” 所以不会有这种压力. 你在以后的爬网页中, 会经常遇到这样的爬取次数的限制 (甚至被封号). 我以前爬 github 时就被限制成一小时只能爬60页.

    具体的代码可以在这里详细观看, 需要注意的是, 我使用的内网进行测试(外网的下载速度变动太大), 在下载网页的地方, 我使用 sleep(0.1) 的功能模拟了网页下载的延迟. 一共下载了我 莫烦 Python 的快400个网页. 因为代码表达的内容我已经用上图展示给大家了, 每一个代码都有50-60行, 我就不粘贴在这里了. 具体的结果, 我们可以总结一下.

    Number of Process   Multiprocessing     Asyncio
        2                   25.5s           7.5s
        4                   15.4s           7.0s
        8                   11.5s           7.2s

    我们发现, 如果 Pool(n) 里面的这个 n 越大, 多进程才能越快, 但是 asyncio 却不会特别受进程数的影响. 一个单线程的东西居然战胜了多进程. 可见异步 asyncio 下载网页的重要性.

    上面介绍的还只是 asyncio 的一小部分功能, 如果想了解更多有关于 asyncio 的使用方法, 请看到 Python 的官方介绍.

    高级爬虫: 让 Selenium 控制你的浏览器帮你爬

    Selenium 是为了测试而出生的. 但是没想到到了爬虫的年代, 它摇身一变, 变成了爬虫的好工具. 让我试着用一句话来概括 Seleninm: 它能控制你的浏览器, 有模有样地学人类”看”网页.
    这里写图片描述
    那么你什么时候会要用到 Selenium 呢? 当你:

    • 发现用普通方法爬不到想要的内容
    • 网站跟你玩”捉迷藏”, 太多 JavaScript 内容
    • 需要像人一样浏览的爬虫

    安装 Selenium

    因为 Selenium 需要操控你的浏览器, 所以安装起来比传统的 Python 模块要多几步. 先在 terminal 或者 cmd 用 pip 安装 selenium.

    # python 2+
    pip install selenium
    
    # python 3+
    pip3 install selenium

    要操控浏览器, 你就要有浏览器的 driver. Selenium 针对几个主流的浏览器都有 driver. 针对 Linux 和 MacOS.

    • Chrome driver
    • Edge driver
    • Firefox driver
    • Safari driver
      Linux 和 MacOS 用户下载好之后, 请将下载好的”geckodriver”文件放在你的计算机的 “/usr/bin” 或 “/usr/local/bin” 目录. 并赋予执行权限, 不会放的, 请使用这条语句.
    sudo cp 你的geckodriver位置 /usr/local/bin
    sudo chmod +x /usr/local/bin/geckodriver

    对于 Windows 用户, 官网上的说法, 好像没有提到要具体怎么操作, 我想, 应该是把 geckodriver 这个文件的位置加在 Windows 的环境变量中(PATH).

    如果你安装有任何的问题, 请在它们的官网上查询解决方案.

    偷懒的火狐浏览器插件

    在这教你用火狐浏览器偷懒的一招, 因为暂时只有火狐上有这个插件. 插件 Katalon Recorder 下载的网址在这
    这里写图片描述

    这个插件能让你记录你使用浏览器的操作. 我以前玩网游, 为了偷懒, 用过一个叫”按键精灵”的东西, 帮我做了很多重复性的工作, 拯救了我的鼠标和键盘, 当然还有我的手指! 看着别人一直在点鼠标, 我心中暗爽~ 这个 Katalon Recorder 插件 + Selenium 就和按键精灵是一个意思. 记录你的操作, 然后你可以让电脑重复上千遍.

    安装好火狐上的这个插件后, 打开它.
    这里写图片描述
    找到插件上的 record, 点它. 然后用火狐登录上 莫烦Python, 开始你的各种点击工作, 比如我的一连串操作是 (强化学习教程->About页面->赞助页面->教程->数据处理->网页爬虫)
    这里写图片描述
    每当你点击的时候, 插件就会记录下你这些点击, 形成一些log. 最后神奇的事情将要发生. 你可以点击 Export 按钮, 观看到帮你生成的浏览记录代码!
    这里写图片描述
    虽然这个代码输出只有 Python2 版本的, 不过不影响. 我们直接将这些圈起来的代码复制. 这将会是 python 帮你执行的行为代码.
    这里写图片描述

    Python 控制浏览器

    好了, 有了这些代码, 我们就能回到 Python. 开始写 Python 的代码了. 这里十分简单! 我将 selenium 绑定到 Chrome 上 webdriver.Chrome(). 你可以绑其它的浏览器.

    from selenium import webdriver
    
    driver = webdriver.Chrome()     # 打开 Chrome 浏览器
    
    # 将刚刚复制的帖在这
    driver.get("https://morvanzhou.github.io/")
    driver.find_element_by_xpath(u"//img[@alt='强化学习 (Reinforcement Learning)']").click()
    driver.find_element_by_link_text("About").click()
    driver.find_element_by_link_text(u"赞助").click()
    driver.find_element_by_link_text(u"教程 ▾").click()
    driver.find_element_by_link_text(u"数据处理 ▾").click()
    driver.find_element_by_link_text(u"网页爬虫").click()
    
    # 得到网页 html, 还能截图
    html = driver.page_source       # get html
    driver.get_screenshot_as_file("./img/sreenshot1.png")
    driver.close()

    我们能得到页面的 html code (driver.page_source), 就能基于这个 code 来爬取数据了. 最后爬取的网页截图就是这样.
    这里写图片描述
    不过每次都要看着浏览器执行这些操作, 有时候有点不方便. 我们可以让 selenium 不弹出浏览器窗口, 让它”安静”地执行操作. 在创建 driver 之前定义几个参数就能摆脱浏览器的身体了.

    from selenium.webdriver.chrome.options import Options
    
    chrome_options = Options()
    chrome_options.add_argument("--headless")       # define headless
    
    driver = webdriver.Chrome(chrome_options=chrome_options)
    ...

    最后同样再截一张图, 证明 driver 真的爬到了这个 “网页爬虫” 教程页面. 不过因为没有出现实体的浏览器, 这个页面大小和上面的图片还是有点差别的.
    这里写图片描述
    Selenium 能做的事还有很多, 比如填 Form 表单, 超控键盘等等. 这个教程不会细说了, 只是个入门, 如果你还想继续深入了解, 欢迎点进去他们的 Python 教学官网.

    最后, Selenium 的优点我们都看出来了, 可以很方便的帮你模拟你的操作, 添加其它操作也是非常容易的, 但是也是有缺点的, 不是任何时候 selenium 都很好. 因为要打开浏览器, 加载更多东西, 它的执行速度肯定没有其它模块快. 所以如果你需要速度, 能不用 Selenium, 就不用吧.

    高级爬虫: 高效无忧的 Scrapy 爬虫库

    前面的教程我们已经学会了如何写出自己的爬虫, 轻轻松松就能写出一个高性能的爬虫. 如果你想更高效的开发, 爬取网页, 记录数据库, Scrapy 是值得一推的. 它是一个爬虫的框架, 而不是一个简单的爬虫. 它整合了爬取, 处理数据, 存储数据的一条龙服务. 如果你只需要偶尔的一两次爬爬网页, 前面的教程已经够了, 如果你需要每天靠爬虫吃饭, Scrapy 还是有必要了解的.
    这里写图片描述
    这个教程教你写出一个 Scrapy 形式的爬虫, 带你入门 Scrapy, 但是 Scrapy 不仅仅只有爬虫, 你需要学习更多. 那学习 Scrapy 的地方, 当然是他们自家网站咯.

    Scrapy 的优势

    Scrapy 是一个整合了的爬虫框架, 有着非常健全的管理系统. 而且它也是分布式爬虫, 但是比我们之前写的那个分布式爬虫高级多了. 下面就是 Scrapy 的框架示意图(来源). 它的管理体系非常复杂. 但是特别高效. 让你又刷网页, 又下载, 同时能处理数据. 简直千手观音呀.
    这里写图片描述

    而且做 Scrapy 的项目, 绝对不是只需要写一个脚本就能解决的. 为了把你带入门, 这次我们只写一个脚本, 只涉及里面的爬虫(spider)部分. 其他的部分你可以在这里深入学习.

    Scrapy 爬虫

    好了, 我们开始今天的简单 Scrapy 教程吧. 首先你得安装 Scrapy. 在 terminal 或者 cmd 使用 pip 安装就好.

    # python 2+
    pip install scrapy
    
    # python 3+
    pip3 install scrapy

    如果安装遇到任何问题, 它们家的网站是个好去处.

    我们之前有做过爬取 莫烦Python 全网的信息. 用多进程 和异步爬取都做过. 这次, 我们就用 Scrapy 来实现这样的一个爬虫. 剧透一下, 做前两个的时候, 代码行数差不多都是 50+ 行, 但是 scrapy 只需要用 20+ 行代码就解决的上面的事. 哈哈, 功能强大吧.

    我们导入 scrapy 模块, 并创建一个 spider 的 class. 并继承 scrapy.Spider, 一定还要给这个 spider 一个名字, 我就用 mofan 好了, 因为是爬 莫烦Python 的. 给定一些初始爬取的网页, 写在 start_urls 里. 这里特别要提的是: 之前我们用 python 的 set 来去除重复的 url, 在 scrapy 中, 这是不需要的, 因为它自动帮你去重. 这可省心多了. 如果你想一次性看到全部代码, 请看到我的 github.

    import scrapy
    
    class MofanSpider(scrapy.Spider):
        name = "mofan"
        start_urls = [
            'https://morvanzhou.github.io/',
        ]
        # unseen = set()
        # seen = set()      # 我们不在需要 set 了, 它自动去重

    接着我们还要定义这个 class 中的一个功能就能完事了. 我们使用 python 的 yield 来返回搜集到的数据 (为什么是yield? 因为在 scrapy 中也有异步处理, 加速整体效率). 这些 title 和 url 的数据, 我们都是用 scrapy 中抓取信息的方式.

    class MofanSpider(scrapy.Spider):
        ...
        def parse(self, response):
            yield {     # return some results
                'title': response.css('h1::text').extract_first(default='Missing').strip().replace('"', ""),
                'url': response.url,
            }
    
            urls = response.css('a::attr(href)').re(r'^/.+?/$')     # find all sub urls
            for url in urls:
                yield response.follow(url, callback=self.parse)     # it will filter duplication automatically

    然后在这个response网页中筛选 urls, 这里我们也不需要使用 urljoin() 这种功能给 url 改变形式. 它在 follow() 这一步会自动检测 url 的格式. (真是省心啊~), 然后对于每个找到的 url, 然后 yield 重新使用 self.parse() 来爬取, 这里又是自动去重! Scrapy 仿佛知道你最不想做什么, 它自动帮你都做好了. 开心~

    最后需要运行的时候有点不同, 你需要在 terminal 或 cmd 中运行这个爬虫. 而且还能帮你保存刚刚 yield 的 {title:, url:} 的结果. runspider 5-2-scrapy.py 就是选择你要跑的这个 Python 文件.

    $ scrapy runspider 5-2-scrapy.py -o res.json

    -o res.json 这个 -o 就是输出的指令, 你可以在那个文件夹中找到一个名字叫 res.json 的文件, 里面存有所有找到的 {title:, url:}.
    这里写图片描述
    其实我们今天只做了 scrapy 中的爬虫, 一个正常的 scrapy 项目还包括有很多其他的内容(见下面). 这个教程就不会细说了, 因为学好 scrapy 还是比较麻烦的. 你可以在上面推荐给你的链接中, 继续深入学习 scrapy.

    tutorial/
        scrapy.cfg            # deploy configuration file
    
        tutorial/             # project's Python module, you'll import your code from here
            __init__.py
    
            items.py          # project items definition file
    
            middlewares.py    # project middlewares file
    
            pipelines.py      # project pipelines file
    
            settings.py       # project settings file
    
            spiders/          # a directory where you'll later put your spiders
                __init__.py
    展开全文
  •   查询了几个基因编号之后发现,网页的排版不会变化,唯一改变的是其具体内容,所以决定首先获取网页所有的内容,然后进行分析,从中提取出自己想要的信息 (三) 工具:   基于python3,Beautifu...
  • 写出最简单的网页爬虫

    千次阅读 2019-07-05 20:51:51
    在我们日常上网浏览网页的时候,经常会看到一些好看的图片,我们就希望把这些图片保存下载,或者用户用来做桌面壁纸,或者用来做设计的素材。我们可以通过python来实现这样一个简单的爬虫功能,把我们想要的代码爬取...
  • 超级简单网页抓取源码,js加载完成抓取源码,抓取动态加载页面的源码。适合初学者,很简单。看懂很容易。页面加载完成抓取源码 适合新手,赚点资源分但是内容绝对有效简单明了。
  • 排名前20的网页爬虫工具

    万次阅读 2019-07-17 13:24:49
    网络爬虫在许多领域都有广泛的应用,它的目标是从网站获取新的数据,并加以存储以方便访问。而网络爬虫工具越来越为人们所熟知,因为它能简化并自动化整个爬虫过程,使每个人都可以轻松访问网络数据资源。 1. ...
  • 一个网页是否更新,是很多爬虫开发人员都会碰到的问题,如果只是一次性抓取来使用,那这个问题无关紧要,抓一遍完事,但是工程中的爬虫通常需要长期运行,如果内容更新,需要及时抓取。 思路1:定期抓取 这是一个...
  • 爬虫系列(一) 网络爬虫简介

    万次阅读 多人点赞 2018-08-09 17:45:00
    最近博主在学习网络爬虫的相关技术(基于 Python 语言),作为一个学习的总结,打算用博客记录下来,也希望和大家分享一下自己在学习过程中的点点滴滴,话不多说,让我们马上开始吧 ^_^
  • 爬虫(一)网络爬虫/相关工具与知识

    万次阅读 多人点赞 2018-06-02 15:12:46
    网络爬虫 网络爬虫(web crawler), 以前经常称为网络蜘蛛(spider), 是按照一定的规则自动浏览... 网络爬虫系统正是通过网页中的超链接信息不断获得网络上的其他页面. 正因为如此, 网络数据采集的过程就像一个爬虫或...
  • 01网络爬虫实现原理详解不同类型的网络爬虫,其实现原理也是不同的,但这些实现原理中,会存在很多共性。在此,我们将以两种典型的网络爬虫为例(即通用网络爬虫和聚焦网络爬虫)...
  • 在这个数据为王的时代,爬虫应用地越来越广泛,对于一个萌新程序员来说如果你要做爬虫,那么Python是你的不二之选。但是对于那些老腊肉的Java程序员(亦或者你是程序媛)想使用Java做爬虫也不是不行,只是没有Python...
  • 网页爬虫java源代码,用广度优先算法以百度为种子开始爬取,有一些bug。
  • 网页爬虫XPath 定位

    千次阅读 2018-06-14 09:48:08
    最近使用到了XPath进行爬虫标签的定位,就将常用的语法总结了一下,方便下次使用时做参考。 1.基本语法 # 倒数第二个 book 元素 //bookstore/book[last()-1] # 除了第一个 book 元素 //bookstore/book...
  • 我的第一个Python爬虫——谈心得

    万次阅读 多人点赞 2018-03-30 19:24:26
    相信各大高校应该都有本校APP或超级课程表之类的软件,在信息化的时代能快速收集/查询自己想要的咨询也是种很重要的能力,所以记下了这篇博客,用于总结我所学到的东西,以及用于记录我的第一个爬虫的初生。...
  • C#实现简单的网页爬虫

    千次阅读 2015-05-23 14:02:18
    using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks;...namespace
  • 如何使用Java语言实现一个网页爬虫

    万次阅读 2016-02-02 22:29:05
    在这篇博文中,我将会使用java语言一步一步的编写一个原型的网页爬虫,其实网页爬虫并没有它听起来那么难。紧跟我的教程,我相信你会在马上学会,一个小时应该可以搞定,之后你就可以享受你所获得的大量数据。这次...
  • Python 网络爬虫入门详解

    万次阅读 多人点赞 2018-01-28 21:01:42
    众所周知,每个网页通常包含其他网页的入口,网络爬虫则通过一个网址依次进入其他网址获取所需内容。 优先申明:我们使用的python编译环境为PyCharm   一、首先一个网络爬虫的组成结构: 爬虫调度程序(程序的...
  • 项目地址 项目介绍 框架 结构 快速启动 修改配置文件 WebDriverPooljava Appjava ComicDriverjava ComicContentServicejava ... 我之前推荐过大家使用seimiagent+seimicrawler,但是经过我多次试验,在爬取任务过多,...
  • 只因写了一段爬虫,公司200多人被抓!

    万次阅读 多人点赞 2019-10-16 09:09:00
    “一个程序员写了个爬虫程序,整个公司200多人被端了。” “不可能吧!” 刚从朋友听到这个消息的时候,我有点不太相信,做为一名程序员来讲,谁还没有写过几段爬虫呢?只因写爬虫程序就被端有点夸张了吧。 朋友...
  • 最基本的网页爬虫(数据采集)

    千次阅读 2014-04-27 01:38:05
    经常看到一些交流网页爬虫
  • 网页抓取:PHP实现网页爬虫方式小结

    万次阅读 多人点赞 2016-05-25 15:49:00
    抓取某一个网页中的内容,需要对DOM树进行解析,找到指定节点后,再抓取我们需要的内容,过程有点繁琐。LZ总结了几种常用的、易于实现的网页抓取方式,如果熟悉JQuery选择器,这几种框架会相当简单。 一、Ganon ...
  • Linux Shell 实现网页爬虫

    千次阅读 2013-11-27 16:58:51
    #!/bin/bash cat ./cidlist | while read CID #从文件cidlist读取CID do { echo $CID  lynx -dump http://cheminfov.informatics.indiana.edu/rest/Chem2Bio2RDF/slap/cid=$CID | grep "see paths" >
  • 需账号密码登陆的网页爬虫

    千次阅读 2019-04-17 15:08:19
    对于普通网页的爬取十分简单,如果网站没有任何反爬机制,只要以下代码就可以实现对于网页的爬取 import requests html = requests.get(url='网址',headers=headers,verify=False) from bs4 import ...
  • Python爬虫100例教程导航帖(已完结)

    万次阅读 多人点赞 2019-01-08 23:40:01
    Python爬虫入门教程导航,目标100篇。 本系列博客争取把爬虫入门阶段的所有内容都包含住,需要你有较好的Python基础知识,当然你完全零基础也可以观看本系列博客。 Python爬虫入门教程,加油!
  • Java 网络爬虫基础入门

    万次阅读 多人点赞 2018-07-13 04:54:00
    课程介绍 大数据环境下,数据分析已由业务驱动转变为数据驱动,网络数据...作为网络爬虫的入门教程,本达人课采用 Java 开发语言,内容涵盖了网络爬虫的原理以及开发逻辑,Java 网络爬虫基础知识,网络抓包介绍,...
  • 情况如下, 现在有一个填写注册信息的页面,当一个用户注册完成之后我...另存一份在我自己建的数据库中,这样的功能应该怎么实现呀,我萌新小白,python语言 刚刚入门,想请教一下这样的问题的思路,欢迎各位大佬指点
  • 中国知网爬虫

    万次阅读 多人点赞 2019-11-21 14:51:25
    中国知网爬虫 一、知网介绍 提起中国知网,如果你曾经写过论文,那么基本上都会与中国知网打交道,因为写一篇论文必然面临着各种查重,当然翟博士除外。但是,本次重点不在于写论文跟查重上,而在于我们要爬取知...
  • 做个网页爬虫很简单, 本文我们来用C/C++语言玩一下, 所用环境是Windows + VC++6.0, 其余平台也类似。 郑重说明:请勿用作非法或者商业用途, 否则后果自负。 我们在新浪上随便找一个博客, 比如:...

空空如也

1 2 3 4 5 ... 20
收藏数 340,675
精华内容 136,270
关键字:

网页爬虫

爬虫 订阅