美文网首页程序员
爬虫学习(01)

爬虫学习(01)

作者: 竹下草 | 来源:发表于2018-12-02 10:32 被阅读11次

    爬虫第一步:获取页面

    一、信息在网络连接层的传递

    (Bob 从Alice那里获得信息)

    1. Bob的电脑发送一个字节流,由信息组成,包含header和body。header包含一个本地路由器MAC地址的直接目的地,和Alice的IP地址的最终目的地。body包含他对接受Alice服务器应用程序的请求。
    2. Bob的本地路由器接收所有这些1和0,并将它们解释为一个包,来自Bob自己的MAC地址,目的地是Alice的IP地址。他的路由器把自己的IP地址作为“来自”IP地址印在包上,然后通过互联网发送出去。
    3. Bob的包穿过几个中间服务器,这些服务器将他的包指向Alice服务器上正确的物理/有线路径。
    4. Alice的服务器在她的IP地址接收数据包。
    5. Alice的服务器读取报头中的包端口目的地,并将其传递给适当的应用程序——web服务器应用程序。(对于web应用程序,包端口目的地几乎总是端口80;这可以看作是包数据的公寓号,而IP地址就像街道地址一样。)
    6. eb服务器应用程序从服务器处理器接收数据流。这些数据说明了如下内容:
    • 这是一个GET请求。
    • 请求以下文件:index.html。
    1. web服务器定位正确的HTML文件,将其打包成一个新的包发送给Bob,并将其发送到本地路由器,以便通过相同的过程将其传输回Bob的机器。

    二、python 虚拟环境

    如果您打算处理多个Python项目,或者您需要一种方法来轻松地将项目与所有关联的库捆绑在一起,或者您担心已安装的库之间可能存在冲突,那么您可以安装一个Python虚拟环境来保持所有内容分离并易于管理。

        $ virtualenv scrapingEnv
    

    创建一个环境scrapingEnv,产生了一个单独的文件夹,包含一套独立的python运行环境,安装的任何库或运行的脚本都将只在该虚拟环境下运行。运行虚拟环境:

        $ cd scrapingEnv/
        $ source bin/activate
        (scrapingEnv)ryan$ pip install beautifulsoup4
        (scrapingEnv)ryan$ python
        > from bs4 import BeautifulSoup
        >
        (scrapingEnv)ryan$ deactivate
    
        ryan$ python
        > from bs4 import BeautifulSoup
        Traceback (most recent call last):
          File "<stdin>", line 1, in <module>
    

    三、urllib.request.urlopen 从一个网络地址获取html文件

    from urllib.request import urlopen
    html = urlopen('http://pythonscraping.com/pages/page1.html')
    print(html.read())
    

    urllib是一个标准的Python库(意味着您不需要安装任何额外的程序来运行这个示例),它包含用于跨web请求数据、处理cookie、甚至更改元数据(如标题和用户代理)的函数。

    urlopen用于跨网络打开远程对象并读取它。它是一个相当通用的函数(它可以轻松地读取HTML文件、图像文件或任何其他文件流)

    四、BeautifulSoup 处理html页面

    BeautifulSoup 通过修复糟糕的HTML和向我们提供表示XML结构的易于遍历的Python对象来帮助格式化和组织混乱的web。

    安装:

        $sudo  apt-get install python-bs4
        or:
        $pip install beautifulsoup4
    

    运行:

    from urllib.request import urlopen
    from bs4 import BeautifulSoup
    
    html = urlopen('http://www.pythonscraping.com/pages/page1.html')
    bs = BeautifulSoup(html.read(), 'html.parser')  # 无需.read()
    print(bs.h1)
    ----------------------Output--------------------------
    <h1>An Interesting Title</h1>
    

    bs = BeautifulSoup(html.read(), 'html.parser')将html文件进行解析,成为一个BeautifulSoup 对象,具有良好的结构层次:

    html → <html><head>...</head><body>...</body></html>
        head → <head><title>A Useful Page<title></head>
            title → <title>A Useful Page</title>
        body → <body><h1>An Int...</h1><div>Loremip...</div></body>
            h1 → <h1>An Interesting Title</h1>
            div → <div>Lorem Ipsum dolor...</div>
    

    然后可以访问不同层次结构:比如获得h1 tag:
    bs.h1;bs.html.h1;bs.body.h1;bs.html.body.h1都可以

    其中参数'html.parser'表示将其以html良好结构进行解析,无需安装直接使用。其他还有'lxml'和'html5lib'两者都是解析方式,不过都需要先下载安装pip install lxmlpip install html5lib.
    lxml格式可以处理更糟糕的html代码,可以进行修复,而且比html.parser更快。缺点在于需要安装,依赖于第三方C库,可能出现易使用型和移植性问题。html5lib是一个非常宽容的解析器,它甚至可以主动更正损坏的HTML。它还依赖于外部依赖,并且比lxml和html.parser都要慢。

    五、可靠地连接并处理异常

    获取页面出错

    1. 服务器上找不到页面 HTMLError
    2. 找不到服务器 URLError
    from urllib.request import urlopen
    from urllib.error import HTTPError
    from urllib.error import URLError
    
    try:
        html_1 = urlopen('http://www.pythonscraping.com/pages/pagenofound.html')
        html_2 = urlopen('https://pythonscrapingthisurldoesnotexist.com')
    except HTTPError as e:
        print(e)
    except URLError as e:
        print('The server could not be found!')
    else:
        print('It Worked!')
    

    tag获取出错

    如果从服务器成功检索到页面,仍然存在页面上的内容与预期不完全一致的问题。每次在BeautifulSoup对象中访问标记时,明智的做法是添加一个检查来确保标记确实存在。如果您试图访问一个不存在的标记,BeautifulSoup将返回一个None对象。如果要继续访问深层tag,则出现None对象的AttributeError错误。

    try:
        badContent = bs.nonExistingTag.anotherTag
    except AttributeError as e:
        print('Tag was not found')
    else:
        if badContent == None:
            print ('Tag was not found')
        else:
            print(badContent)
    

    爬虫第二步:HTML 解析和信息提取

    注意:一般不要去通过使用多行语句层层深入来找到所要的信息。可能因为页面的变化而出错

    bs.find_all('table')[4].find_all('tr')[2].find('td').find_all('div')[1].find('a')
    

    接下来的beautifulsoup对象的信息提取都建立在获取页面内容之后:

    from urllib.request import urlopen
    from bs4 import BeautifulSoup
    
    html = urlopen('http://www.pythonscraping.com/pages/page1.html')
    bs = BeautifulSoup(html.read(), 'html.parser')
    

    一、通过属性匹配获得特定tag

    nameList = bs.findAll('span', {'class':'green'})
    for name in nameList:
        print(name.get_text())
    
    1. bs.find_all(tagName, tagAttributes)来获取页面上所有标记的列表,而不仅仅是第一个标记。
    2. tagName.get_text(),以便将内容从标记中分离出来。

    find() 和 find_all()

    BeautifulSoup的find()和find_all()是您可能最常用的两个函数。有了它们,您可以很容易地过滤HTML页面,根据它们的各种属性找到所需标记的列表,或者单个标记。

    find_all(tag, attributes, recursive, text, limit, keywords)
    find(tag, attributes, recursive, text, keywords)
    

    tag: 可以传递标记的字符串名称,甚至Python字符串标记名称列表

    .find_all(['h1','h2','h3','h4','h5','h6'])
    

    attributes:获取属性的Python字典,并匹配包含这些属性之一的标记

    .find_all('span', {'class':{'green', 'red'}})
    

    recursive:布尔值。您希望文档深入到什么程度?如果recursive被设置为True, find_all函数将查找与参数匹配的标记的子标记和子标记的子标记。如果它是假的,它将只查看文档中的顶级标记。默认情况下,find_all递归工作(recursive设置为True)

    text:根据标记的文本内容进行匹配,而不是根据标记本身的属性进行匹配

    nameList = bs.find_all(text='the prince')
    print(len(nameList))
    

    keywords:允许您选择包含特定属性或属性集的标记

    title = bs.find_all(id='title', class_='text')
    

    但keywords可以用attributes参数替代:

    bs.find_all('', {'id':'text','class':'text'})
    

    二、通过html组织结构获取tag

    children and descendants 直接子代和所有后辈

    在BeautifulSoup库和许多其他库中,在子库和后代库之间有一个区别:就像在人类族谱中一样,子库总是恰好比父库低一个标记,而后代库可以在父库下面的树中的任何级别。
    一般来说,BeautifulSoup函数总是处理选中的当前标记的后代。例如,bs.body.h1选择第一个h1标记,它是body标记的后代。它不会找到位于主体外部的标记。类似地,bs.div.find_all('img')将在文档中找到第一个div标记,然后检索该div标记的后代的所有img标记的列表.
    如果您希望只找到子代,可以使用.children标记

    for child in bs.find('table',{'id':'giftList'}).children:
        print(child)
    

    所有后辈则使用.descendants

    for all_child in bs.find('table',{'id':'giftList'}).descendants:
        print(all_child)
    

    siblings同层次兄弟姐妹

    for sibling in bs.find('table',{'id':'giftList'}).tr.next_siblings:
        print(sibling)
    

    当你得到一个对象的兄弟siblings对象时,该对象本身将不会包含在列表中。正如函数名所暗示的,它只调用下面同层次的所有兄弟姐妹。例如,如果在列表中间选择一行,并对其调用next_siblings,则只返回后续的兄弟姐妹。因此,通过选择标题行并调用next_siblings,可以选择表中的所有行.

    作为对next_siblings的补充,previous_siblings函数通常很有用,如果您希望得到的同胞标记列表的末尾有一个易于选择的标记。previous_siblings返回同层次的所有兄弟姐妹

    也有next_sibling和previous_sibling函数,它们执行的函数几乎与next_sibling和previous_sibling相同,只不过它们返回的是单个标记,而不是它们的列表

    parent 父代标记

    bs.find('img',{'src':'../img/gifts/img1.jpg'}).parent.previous_sibling.get_text()
    

    三、正则表达式

    import re
    
    images = bs.find_all('img',
        {'src':re.compile('\.\.\/img\/gifts/img.*\.jpg')})
    for image in images:
        print(image['src'])
    

    只打印以../img/gifts/img开头并且以.jpg结尾的相对图像路径。 输出结果如下:

    ../img/gifts/img1.jpg
    ../img/gifts/img2.jpg
    ../img/gifts/img3.jpg
    ../img/gifts/img4.jpg
    ../img/gifts/img6.jpg
    

    四、获取属性

    使用标记对象,Python属性列表可以通过调用以下命令自动访问:
    myTag.attrs
    这实际上返回了Python dictionary对象,可以使用以下行找到图像的源位置:
    myTag.attrs['src']

    五、Lambda 表达式

    BeautifulSoup允许您将某些类型的函数作为参数传递到find_all函数中。
    唯一的限制是这些函数必须以标记对象作为参数并返回布尔值。beautifulsoup遇到的每个标记对象都在这个函数中求值,值为True的标记将被返回,其余的将被丢弃。
    bs.find_all(lambda tag: len(tag.attrs) == 2)返回所有具有两个属性的标记

    Lambda函数非常有用,您甚至可以使用它们来替换现有的BeautifulSoup函数
    bs.find_all(lambda tag: tag.get_text() =='Or maybe he\'s only resting?')
    等同于:
    bs.find_all('', text='Or maybe he\'s only resting?')

    相关文章

      网友评论

        本文标题:爬虫学习(01)

        本文链接:https://www.haomeiwen.com/subject/qqzdcqtx.html