【问题标题】:How can I find specific text in a website's HTML code with Python and BeautifulSoup?如何使用 Python 和 BeautifulSoup 在网站的 HTML 代码中查找特定文本?
【发布时间】:2020-08-02 07:36:27
【问题描述】:

这里对 HTML 和 Python 来说是全新的。我想用 Python 抓取一个网站来查找拍卖数据。我想查找所有带有“lb, lbs., pound”等文字的列表。这是我感兴趣的列表 HTML 代码示例:

    <a class="product" href="/Item/91150404">
    <div class="title">
                30.00 LB Lego Mini Figures Lego People Grab Bag
                                        <br>Bids: 7                                    </div> </a>

我想出了如何使用 title_all 变量获取所有“title”标签的 ResultSet,但我想进一步过滤所有拍卖列表以仅显示名称中带有“LB”的列表。我已经阅读了 BeautifulSoup 文档,我能做的最好的就是返回一个空白列表 []。这是我的 Python 代码:

import requests
import re
from bs4 import BeautifulSoup

url = 'https://www.shopgoodwill.com/Listings?st=&sg=&c=388&s=&lp=0&hp=999999&sbn=false&spo=false&snpo=false&socs=false&sd=false&sca=false&caed=4/18/2020&cadb=7&scs=false&sis=false&col=0&p=1&ps=40&desc=false&ss=0&UseBuyerPrefs=true'
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')

title_all=soup.findAll(True,class_=['title'])
result=soup.findAll('div', text = re.compile('LB'),attrs = {'class' : 'title'})
print(result)
#does not work

我也尝试在这里阅读类似的问题并实施答案,但我遇到了困难。任何帮助将不胜感激!我正在使用 Python 3.7.3 和 BeautifulSoup 4。谢谢!

【问题讨论】:

    标签: python html web-scraping beautifulsoup


    【解决方案1】:

    代替:

    text=re.compile('LB')
    

    试试:

    string=re.compile('LB')
    

    Documentation

    【讨论】:

    • 那个结果是一个空列表,不知道为什么它不起作用。
    【解决方案2】:
    from bs4 import BeautifulSoup
    import datetime as dt
    import requests
    
    url = 'https://www.shopgoodwill.com/Listings?st=&sg=&c=388&s=&lp=0&hp=999999&sbn=false&spo=false&snpo=false&socs=false&sd=false&sca=false&caed=4/18/2020&cadb=7&scs=false&sis=false&col=0&p=1&ps=40&desc=false&ss=0&UseBuyerPrefs=true'
    r = requests.get(url)
    bs = BeautifulSoup(r.text, "html.parser")
    
    # Gathering products.
    bs_products = bs.findAll("a", {"class": "product"})
    
    # Gathering listing information for each product.
    products = [] 
    for product in bs_products:
        price_str = product.find("div", {"class": "price"}).text.strip()
        price_int = int(''.join(filter(lambda i: i.isdigit(), price_str)))
        
        product = {"img": product.find("img", {"class": "lazy-load"}).get("data-src"), 
                   "num": int(product.find("div", {"class": "product-number"}).text.split(":")[1]), 
                   "title": product.find("div", {"class": "title"}).next_element.strip(),
                   "time_left": dt.datetime.strptime(product.find("div", {"class": "timer"}).get("data-countdown"), "%m/%d/%Y %I:%M:%S %p"),
                   "price": price_int}
        
        products.append(product)
    
    filter_LB = list(filter(lambda product: "LB" in product['title'], products))
    print(filter_LB)
    

    输出:

    [{'img': 'https://sgwproductimages.azureedge.net/109/4-16-2020/56981071672752ssdt-thumb.jpg',
      'num': 91150404,
      'title': '30.00 LB Lego Mini Figures Lego People Grab Bag',
      'time_left': datetime.datetime(2020, 4, 21, 19, 20),
      'price': 444500},
     {'img': 'https://sgwproductimages.azureedge.net/5/4-14-2020/814151314749m.er-thumb.jpg',
      'num': 91000111,
      'title': '20 LBS of Bulk Loose Lego Pieces',
      'time_left': datetime.datetime(2020, 4, 19, 18, 6),
      'price': 4600}]
    

    我建议做的是利用 BS4 来实现它的用途——scraping——然后​​利用 Python 来过滤你的对象。我不会反对BS4可以过滤的说法,但是,我一直发现最好先实现一个通用的解决方案,然后在需要的情况下处理细节。

    如果您不熟悉filter,请查看文档here。如果你不知道lambda 是什么,它是一个用一行写的函数。所有filter 都会遍历您的对象,并应用给定的lambda 函数。无论对象在lambda 中返回Truefilter 都会返回它。

    def func(a):
        return a + 2
    
    func(4) # >>> 6
    
    func = lambda a: a + 2
    
    func(4) # >>> 6
    

    编程愉快! :)


    参考资料:


    编辑:为了下面的讨论。假设我们想要过滤数字始终大于或等于 5。我们可以通过多种方式实现:

    l = [1, 2, 3, 4, 5, 6, 7]
    
    # Traditional filtering way. Makes sense.
    filtered_l = []
    for i in l:
        if i >= 5:
            filtered_l.append(i)
    
    # Lambda + Filter way
    filtered_l = list(filter(lambda i: i >= 5, l))
    
    # Function + Filter Way
    def filtering(i): # Notice this function returns either True or False. 
        return i >= 5
    filtered_l = list(filter(filtering, l))
    

    您可能会问我们为什么要做list(filter()) 而不是简单的filter()。那是因为filter 返回一个iterable,它原本不是一个列表。这是一个object that you can intereate 通过。因此,我们通过将filter 转换为列表来提取资源。同样,您可以将 list 转换为可迭代对象(这为您提供额外的功能和控制):

    l = [1, 2, 3, 4, 5]
    iter_l = iter(l) # >>> <list_iterator object at 0x10aa9ee50>
    
    next(iter_l) # >>> 1
    next(iter_l) # >>> 2
    next(iter_l) # >>> 3
    next(iter_l) # >>> 4
    next(iter_l) # >>> 5
    
    next(iter_l)
    
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    StopIteration
    

    您可能会问“为什么要使用iter 而不是简单地使用列表?”答案是因为你可以在类中重载__iter____next__ 功能,使它们成为可迭代的类(你可以在其中调用for循环的类):

    import random
    
    class RandomIterable:
        def __iter__(self):
            return self
    
        def __next__(self):
            if random.choice(["go", "go", "stop"]) == "stop":
                raise StopIteration  # signals "the end"
            return 1
    

    这允许我们遍历类本身:

    for eggs in RandomIterable():
        print(eggs)
    

    或者,就像您在 filter 中使用的那样,只需获取列表:

    list(RandomIterable())
    >>> [1]
    

    在这种情况下,它将返回您随机选择单词 stop 的时间量(由每个 1 标记)。如果返回的是[1, 1],则stop 被连续选中两次。当然这是一个愚蠢的例子,但希望现在你能看到 listfilterlambda 如何在 Python 中一起过滤列表(也称为可迭代对象)。

    【讨论】:

    • 在这种情况下使用 lambda 是因为它是一种更有效/优雅的递增方式,而不是外部 for 循环中的嵌套 for 循环?另外,也许是因为它是一个开放式增量?
    • lambda 被使用(在这两种情况下),因为函数 filter 采用参数 filter(function, iterable) 其中function 是一个返回 truefalse 的函数,并且 iterable 是任何可通过标准 for i in iterable 迭代的 Python typelisttuple 等)。
    • 因此,例如,以下行 filter_LB = list(filter(lambda product: "LB" in product['title'], products)) 等价于(将 ; 替换为适当缩进的新行):filter_LB = []; for product in product['title']; if "LB" in product:; filter_LB.append(product)
    • 为了更直接地回答您的问题,使用filterlambda 更高效(在某些情况下)、更优雅、更Pythonic。您正在使用 Python 提供给您的工具来执行您的任务,而不是诉诸于编写自己的类似过滤器的功能。希望这可以解释 :) 用您自己的代码替换 lambda 在技术上或逻辑上没有任何问题。
    • 进行了编辑以解释listfilterlambda 之间的概念。
    【解决方案3】:

    另一种解决方案。

    from simplified_scrapy import SimplifiedDoc,req,utils
    # url = 'https://www.shopgoodwill.com/Listings?st=&sg=&c=388&s=&lp=0&hp=999999&sbn=false&spo=false&snpo=false&socs=false&sd=false&sca=false&caed=4/18/2020&cadb=7&scs=false&sis=false&col=0&p=1&ps=40&desc=false&ss=0&UseBuyerPrefs=true'
    # html = req.get(url)
    # url = 'https://www.shopgoodwill.com/Listings?st=&sg=&c=388&s=&lp=0&hp=999999&sbn=false&spo=false&snpo=false&socs=false&sd=false&sca=false&caed=4/18/2020&cadb=7&scs=false&sis=false&col=0&p=1&ps=40&desc=false&ss=0&UseBuyerPrefs=true'
    # html = requests.get(url).text
    html = '''
    <a class="product" href="/Item/91150404">
        <div class="title">
                    30.00 LB Lego Mini Figures Lego People Grab Bag
                                            <br>Bids: 7
        </div>
    </a>
    '''
    doc = SimplifiedDoc(html)
    title_all = doc.getElementsByReg('( LB | LBS )',tag="div").text
    print(title_all)
    

    结果:

    ['30.00 LB Lego Mini Figures Lego People Grab Bag Bids: 7']
    

    这里有更多示例。 https://github.com/yiyedata/simplified-scrapy-demo/tree/master/doc_examples

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-10-15
      • 2023-03-16
      • 1970-01-01
      • 2016-09-07
      • 1970-01-01
      • 1970-01-01
      • 2010-10-26
      相关资源
      最近更新 更多