跳转至

BS4解析HTML

BS4是一个外部的工具,需要通过pip引入使用,具体操作可百度

BS4使用前需要进行的操作

from bs4 import BeautifulSoup
def get_bs(url,timeout):#请求一个网页,以HTML的格式返回,如果超时或者网页我无法访问,则返回一个None
    header={
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 Edg/107.0.1418.35"}
    request = urllib.request.Request(url=url,headers=header)
    try:
        response = urllib.request.urlopen(request,timeout=timeout)
        html = response.read().decode("utf-8")
    except Exception:
        return None
    bs= BeautifulSoup(html,"html.parser")#bs就是已经初始化好的对象

经过这一步是,我们已经得到了一个BeautifulSoup的对象了。

BS4抓取数据

父子元素直接访问

html经过解析后形成了一个树状结构。例如:

bs.title就可以直接获取HTML中的标题。也就是获取了的标签;</p> <p><code>bs.a</code>可以获取HTML中的第一个<a>的标签;</p> <p><code>bs.javascript</code>可以获取HTML中的第一个<javascript>的标签。</p> <blockquote> <p>但是要注意,这里获取的标签是包括子标签的,并且注意,这个标签并不是普通的字符串,而是一个对象,也是一个<strong>TAG</strong>类的实例。我们可以通过这个对象访问它的父和子元素</p> <p>访问tag对象的父元素:<strong>tag.parent</strong></p> <p>访问tag对象的子元素:<strong>tag.contents</strong></p> <p><em>PS:子元素不一定只有一个,因此返回值是一个列表,不可以直接使用,可以遍历或者通过下标访问</em></p> </blockquote> <p>如果你试着<code>print(bs.title)</code>的话,你可能对看到一下结果:</p> <blockquote> <p>C:\\<title>这是标题<title></p> </blockquote> <p>没错,如果直接输出TAG对象的话,Python会把他的标签连同内容一起输出(可能还有子元素),也就是从开始标签的一直到结束标签的全部输出。那么如果只想要标签里面的数据呢,怎么办?</p> <p>BS4当然有办法,对于TAG对象来讲,其中的内容也是他的一个属性,那就直接访问咯!</p> <blockquote> <p>TAG.string</p> </blockquote> <p>这便是TAG对象的内容,这里面也不会有子元素。可以<strong><em>难道这个就是一个stirng数据吗?</em></strong>我们可以使用<code>type()</code>函数来看一看?<code>print(TAG.string)</code>让我们看看他到底是什么类型。</p> <blockquote> <p><class ‘bs4.element.NavigableString’></p> </blockquote> <p>是的,他并不是一个string类型,仍然是<strong>element中的一个小类</strong>,因此咱们还可以使用<code>NavigableString.parent</code>来获取他的父元素,也就是那个<strong><em>TAG</em></strong></p> <h2 id="beautifulsoup4">BeautifulSoup4中的类<a class="headerlink" href="#beautifulsoup4" title="Permanent link">¶</a></h2> <h3 id="element">Element<a class="headerlink" href="#element" title="Permanent link">¶</a></h3> <p>这是所有类的父类</p> <h5 id="_2">属性<a class="headerlink" href="#_2" title="Permanent link">¶</a></h5> <ul> <li><strong>name</strong>:标签名称,其实也就是标签是什么类型,例如对于<code><title></code>标签,他的<strong>name</strong>就是<em>title</em></li> <li><strong>parent</strong>:父元素(树状结构)</li> <li><strong>contents</strong>:子元素(list结构)</li> </ul> <h3 id="tag">TAG<a class="headerlink" href="#tag" title="Permanent link">¶</a></h3> <p>TAG类是BeautifulSoup包中所有的类,他是在文档被解析后所生成的类</p> <h5 id="_3">属性<a class="headerlink" href="#_3" title="Permanent link">¶</a></h5> <ul> <li><strong>string</strong>:元素中的内容,一般也就是我们需要抓取的数据</li> <li><strong>attrs</strong>:元素的属性,因为属性不一定只有一个,因此返回的是个字典(dict),也就是<strong>键值对组</strong>,就相当于<em>maps<keys,values></em>,比如<code>bs.a.attrs</code>可以获取他的<code>`href、class</code>,不过可以才想到,他应该和上面的<strong>stirng</strong>一样,也是一个对象,可以通过<code>href.parent</code>访问他的父元素,也就是属性所有者,这便是<strong>树状结构</strong>的体现。</li> </ul> <h3 id="beautifulsoup">BeautifulSoup<a class="headerlink" href="#beautifulsoup" title="Permanent link">¶</a></h3> <h5 id="_4">属性<a class="headerlink" href="#_4" title="Permanent link">¶</a></h5> <ul> <li><strong>title</strong>:就是document</li> <li>其中包含所有数据,可以直接访问,例如<code>beautifulsoup.a</code>,可以拿到第一个a标签。</li> </ul> <h2 id="_5">节点获取(条件节点)<a class="headerlink" href="#_5" title="Permanent link">¶</a></h2> <h3 id="1-find_all">1. find_all()<a class="headerlink" href="#1-find_all" title="Permanent link">¶</a></h3> <p>查找所有,比通过树状结构访问获得的元素更多</p> <ul> <li>通过字符串查找:</li> </ul> <p><code>find_all("a")</code>找到所有<code>a</code>的标签</p> <p>还可以使用列表:<code>find_all(["a","b"])</code>会将所有a,b标签返回</p> <ul> <li>正则表达式查找:</li> </ul> <p><code>find_all(re.compile("a"))</code>查找所有标签中含有a的元素,比如<code>head</code>、<code>meta</code>等当然不止这些,如果他的属性里面也含有<em>a</em>也会被包含进来,比如<code>href</code>之中如果含有a字样,也会包含。</p> <ul> <li>自定义查找:传入的不是参数,而是函数,没错,要知道python可是c语言家族的,所以函数做参数也无可厚非,那应该怎么做呢?</li> </ul> <div class="highlight"><pre><span></span><code><a id="__codelineno-1-1" name="__codelineno-1-1" href="#__codelineno-1-1"></a><span class="k">def</span> <span class="nf">name_is_exist</span><span class="p">(</span><span class="n">tag</span><span class="p">):</span> <a id="__codelineno-1-2" name="__codelineno-1-2" href="#__codelineno-1-2"></a> <span class="k">return</span> <span class="n">tag</span><span class="o">.</span><span class="n">has_attr</span><span class="p">(</span><span class="s2">"name"</span><span class="p">)</span> <a id="__codelineno-1-3" name="__codelineno-1-3" href="#__codelineno-1-3"></a><span class="n">beautiful</span><span class="o">.</span><span class="n">find_all</span><span class="p">(</span><span class="n">name_is_exist</span><span class="p">)</span> <a id="__codelineno-1-4" name="__codelineno-1-4" href="#__codelineno-1-4"></a><span class="c1">#这样就会把所有传入并返回true的所有标签放回(list),但是就上面这个算法,还有个更优的方法</span> <a id="__codelineno-1-5" name="__codelineno-1-5" href="#__codelineno-1-5"></a><span class="n">beautiful</span><span class="o">.</span><span class="n">find_all</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> </code></pre></div> <ul> <li>进阶正则表达式(<em>kwargs</em>):上面一个正则表达式的查找是一种十分无脑的东西,因为他会无脑的在两个尖括号里面直接查找,没有限制;如果我们想指定哪个属性的值符合正则怎么办?</li> </ul> <div class="highlight"><pre><span></span><code><a id="__codelineno-2-1" name="__codelineno-2-1" href="#__codelineno-2-1"></a><span class="n">beautiful</span><span class="o">.</span><span class="n">find_all</span><span class="p">(</span><span class="n">class_</span><span class="o">=</span><span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span><span class="s2">"\d"</span><span class="p">))</span> <a id="__codelineno-2-2" name="__codelineno-2-2" href="#__codelineno-2-2"></a><span class="c1">#可以查找所有类名中含数组的标签</span> </code></pre></div> <ul> <li>限定搜索:</li> </ul> <p>使用<code>find_all()</code>时传参<code>limit=4</code></p> <h3 id="2cssselect">2.css选择器(select)<a class="headerlink" href="#2cssselect" title="Permanent link">¶</a></h3> <div class="highlight"><pre><span></span><code><a id="__codelineno-3-1" name="__codelineno-3-1" href="#__codelineno-3-1"></a><span class="n">beautiful</span><span class="o">.</span><span class="n">select</span><span class="p">(</span><span class="s2">"div.title"</span><span class="p">)</span> <a id="__codelineno-3-2" name="__codelineno-3-2" href="#__codelineno-3-2"></a><span class="c1">#返回所有div下的title的标签</span> <a id="__codelineno-3-3" name="__codelineno-3-3" href="#__codelineno-3-3"></a><span class="n">beautiful</span><span class="o">.</span><span class="n">select</span><span class="p">(</span><span class="s2">".display"</span><span class="p">)</span> <a id="__codelineno-3-4" name="__codelineno-3-4" href="#__codelineno-3-4"></a><span class="c1">#返回所有display类的标签</span> <a id="__codelineno-3-5" name="__codelineno-3-5" href="#__codelineno-3-5"></a><span class="n">beautiful</span><span class="o">.</span><span class="n">select</span><span class="p">(</span><span class="s2">"#username"</span><span class="p">)</span> <a id="__codelineno-3-6" name="__codelineno-3-6" href="#__codelineno-3-6"></a><span class="c1">#返回所有id为username的标签</span> <a id="__codelineno-3-7" name="__codelineno-3-7" href="#__codelineno-3-7"></a><span class="n">beautiful</span><span class="o">.</span><span class="n">select</span><span class="p">(</span><span class="s2">"a[class='bri']"</span><span class="p">)</span> <a id="__codelineno-3-8" name="__codelineno-3-8" href="#__codelineno-3-8"></a><span class="c1">#返回所有class为bri的a标签</span> <a id="__codelineno-3-9" name="__codelineno-3-9" href="#__codelineno-3-9"></a><span class="n">beautiful</span><span class="o">.</span><span class="n">select</span><span class="p">(</span><span class="s2">"div > a"</span><span class="p">)</span> <a id="__codelineno-3-10" name="__codelineno-3-10" href="#__codelineno-3-10"></a><span class="c1">#返回所有div下的a标签</span> <a id="__codelineno-3-11" name="__codelineno-3-11" href="#__codelineno-3-11"></a><span class="n">beautiful</span><span class="o">.</span><span class="n">select</span><span class="p">(</span><span class="s2">"#cs ~ ul"</span><span class="p">)</span> <a id="__codelineno-3-12" name="__codelineno-3-12" href="#__codelineno-3-12"></a><span class="c1">#返回所有和id为cs的标签同一级的ul标签</span> <a id="__codelineno-3-13" name="__codelineno-3-13" href="#__codelineno-3-13"></a><span class="c1">#详细语法见css选择器</span> </code></pre></div> </article> </div> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type="button" class="md-top md-icon" data-md-component="top" hidden> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8v12Z"/></svg> 回到页面顶部 </button> </main> <footer class="md-footer"> <nav class="md-footer__inner md-grid" aria-label="页脚" > <a href="../../%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1/FastJson%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1/" class="md-footer__link md-footer__link--prev" aria-label="上一页: FastJSON"> <div class="md-footer__button md-icon"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11h12Z"/></svg> </div> <div class="md-footer__title"> <span class="md-footer__direction"> 上一页 </span> <div class="md-ellipsis"> FastJSON </div> </div> </a> <a href="../Docker%E5%92%8CDockerCompose%E7%9A%84%E4%BD%BF%E7%94%A8/" class="md-footer__link md-footer__link--next" aria-label="下一页: Docker"> <div class="md-footer__title"> <span class="md-footer__direction"> 下一页 </span> <div class="md-ellipsis"> Docker </div> </div> <div class="md-footer__button md-icon"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M4 11v2h12l-5.5 5.5 1.42 1.42L19.84 12l-7.92-7.92L10.5 5.5 16 11H4Z"/></svg> </div> </a> </nav> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class="md-copyright"> <div class="md-copyright__highlight"> Copyright © 2024 leotan2004 </div> </div> </div> </div> </footer> </div> <div class="md-dialog" data-md-component="dialog"> <div class="md-dialog__inner md-typeset"></div> </div> <script id="__config" type="application/json">{"base": "../..", "features": ["header.autohide", "navigation.instant", "navigation.tracking", "content.code.annotate", "toc.integrate", "toc.follow", "navigation.path", "navigation.top", "navigation.tabs", "navigation.prune", "navigation.footer", "navigation.tabs.sticky", "navigation.sections", "content.code.copy", "navigation.indexes", "search.share", "search.suggest", "search.highlight"], "search": "../../assets/javascripts/workers/search.b8dbb3d2.min.js", "translations": {"clipboard.copied": "\u5df2\u590d\u5236", "clipboard.copy": "\u590d\u5236", "search.result.more.one": "\u5728\u8be5\u9875\u4e0a\u8fd8\u6709 1 \u4e2a\u7b26\u5408\u6761\u4ef6\u7684\u7ed3\u679c", "search.result.more.other": "\u5728\u8be5\u9875\u4e0a\u8fd8\u6709 # \u4e2a\u7b26\u5408\u6761\u4ef6\u7684\u7ed3\u679c", "search.result.none": "\u6ca1\u6709\u627e\u5230\u7b26\u5408\u6761\u4ef6\u7684\u7ed3\u679c", "search.result.one": "\u627e\u5230 1 \u4e2a\u7b26\u5408\u6761\u4ef6\u7684\u7ed3\u679c", "search.result.other": "# \u4e2a\u7b26\u5408\u6761\u4ef6\u7684\u7ed3\u679c", "search.result.placeholder": "\u952e\u5165\u4ee5\u5f00\u59cb\u641c\u7d22", "search.result.term.missing": "\u7f3a\u5c11", "select.version": "\u9009\u62e9\u5f53\u524d\u7248\u672c"}}</script> <script src="../../assets/javascripts/bundle.a7c05c9e.min.js"></script> <script src="../../javascripts/mathjax.js"></script> <script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script> <script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script> </body> </html>