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 中返回True,filter 都会返回它。
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 被连续选中两次。当然这是一个愚蠢的例子,但希望现在你能看到 list、filter 和 lambda 如何在 Python 中一起过滤列表(也称为可迭代对象)。