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 для того, для чего он предназначен - утилизацию - и затем использовать Python для фильтрации ваших объектов. Я не буду возражать против утверждения, что BS4 может фильтровать, но я всегда обнаруживал, что лучше сначала реализовать общее решение, а затем разобраться со спецификой в необходимых случаях.
Если вы не знакомы с filter
, ознакомьтесь с документацией здесь . Если вы не знаете, что такое lambda
, это функция, записанная в одну строку. Все filter
проходит через ваши объекты oop и применяет данную функцию lambda
. Какой бы объект не вернул True
внутри lambda
, 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
, который изначально не был списком. Это объект, через который вы можете взаимодействовать . Поэтому мы извлекаем ресурсы 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]
, который в этом случае вернет вам количество времени ( отмеченные 1
), что слово stop
было выбрано случайным образом. Если возвращение было [1, 1]
, то stop
было выбрано дважды подряд. Конечно, это глупый пример, но, надеюсь, теперь вы видите, как list
, filter
и lambda
работают вместе для фильтрации списков (или итерируемых) в Python.