как очистить данные из шопи, используя красивый суп - PullRequest
1 голос
/ 28 мая 2020

В настоящее время я студент, где в настоящее время изучаю beautifulsoup, так что мой лектор, как и я, собирает данные из магазина, однако я не могу очистить детали продуктов. В настоящее время я пытаюсь очистить данные из https://shopee.com.my/shop/13377506/search?page=0&sortBy=sales. Я только хочу поскрести название и цену продуктов. может кто-нибудь сказать мне, почему я не могу очистить данные с помощью beautifulsoup?

Вот мой код:

from requests import get
from bs4 import BeautifulSoup

url = "https://shopee.com.my/shop/13377506/search?page=0&sortBy=sales"
response= get (url)
soup=BeautifulSoup(response.text,'html.parser')
print (soup)

Ответы [ 3 ]

1 голос
/ 02 июня 2020

Этот вопрос немного сложен ( для python новичков ), потому что он включает комбинацию селена (для просмотра без головы) и beautifulsoup (для извлечения данных html). Более того, проблема становится сложной, потому что объектная модель документа (DOM) заключена в javascripting. Мы знаем, что javascript существует, потому что мы получаем пустой ответ от веб-сайта при доступе только с использованием beautifulsoup, например, for item_n in soup.find_all('div', class_='_1NoI8_ _16BAGk'): print(item_n.get_text())

Следовательно, для извлечения данных с такой веб-страницы, которая имеет язык сценариев, управляющий ее DOM , мы должны использовать селен для просмотра без заголовка ( это сообщает веб-сайту, что браузер обращается к нему ). Мы также должны использовать какой-то параметр задержки (, который сообщает веб-сайту, что к нему обращается человек ). Для этого поможет функция WebdriverWait() из библиотеки селена.

Теперь я представляю фрагменты кода, объясняющие этот процесс.

Сначала импортируйте необходимые библиотеки

from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import TimeoutException
from time import sleep

Далее инициализируем настройки для безголового браузера. Я использую chrome.

# create object for chrome options
chrome_options = Options()
base_url = 'https://shopee.com.my/shop/13377506/search?page=0&sortBy=sales'

# set chrome driver options to disable any popup's from the website
# to find local path for chrome profile, open chrome browser
# and in the address bar type, "chrome://version"
chrome_options.add_argument('disable-notifications')
chrome_options.add_argument('--disable-infobars')
chrome_options.add_argument('start-maximized')
chrome_options.add_argument('user-data-dir=C:\\Users\\username\\AppData\\Local\\Google\\Chrome\\User Data\\Default')
# To disable the message, "Chrome is being controlled by automated test software"
chrome_options.add_argument("disable-infobars")
# Pass the argument 1 to allow and 2 to block
chrome_options.add_experimental_option("prefs", { 
    "profile.default_content_setting_values.notifications": 2
    })
# invoke the webdriver
browser = webdriver.Chrome(executable_path = r'C:/Users/username/Documents/playground_python/chromedriver.exe',
                          options = chrome_options)
browser.get(base_url)
delay = 5 #secods

Затем я объявляю пустые переменные списка для хранения данных.

# declare empty lists
item_cost, item_init_cost, item_loc = [],[],[]
item_name, items_sold, discount_percent = [], [], []
while True:
    try:
        WebDriverWait(browser, delay)
        print ("Page is ready")
        sleep(5)
        html = browser.execute_script("return document.getElementsByTagName('html')[0].innerHTML")
        #print(html)
        soup = BeautifulSoup(html, "html.parser")

        # find_all() returns an array of elements. 
        # We have to go through all of them and select that one you are need. And than call get_text()
        for item_n in soup.find_all('div', class_='_1NoI8_ _16BAGk'):
            print(item_n.get_text())
            item_name.append(item_n.text)

        # find the price of items
        for item_c in soup.find_all('span', class_='_341bF0'):
            print(item_c.get_text())
            item_cost.append(item_c.text)

        # find initial item cost
        for item_ic in soup.find_all('div', class_ = '_1w9jLI QbH7Ig U90Nhh'):
            print(item_ic.get_text())
            item_init_cost.append(item_ic.text)
        # find total number of items sold/month
        for items_s in soup.find_all('div',class_ = '_18SLBt'):
            print(items_s.get_text())
            items_sold.append(item_ic.text)

        # find item discount percent
        for dp in soup.find_all('span', class_ = 'percent'):
            print(dp.get_text())
            discount_percent.append(dp.text)
        # find item location
        for il in soup.find_all('div', class_ = '_3amru2'):
            print(il.get_text())
            item_loc.append(il.text)

        break # it will break from the loop once the specific element will be present. 
    except TimeoutException:
        print ("Loading took too much time!-Try again")

После этого я использую функцию zip для объединения разные элементы списка.

rows = zip(item_name, item_init_cost,discount_percent,item_cost,items_sold,item_loc)

Наконец, я записываю эти данные в dis c,

import csv
newFilePath = 'shopee_item_list.csv'
with open(newFilePath, "w") as f:
    writer = csv.writer(f)
    for row in rows:
        writer.writerow(row)

В качестве хорошей практики рекомендуется закрыть браузер без головы после выполнения задачи. полный. И поэтому я кодирую его как

# close the automated browser
browser.close()

Результат

Nestle MILO Activ-Go Chocolate Malt Powder (2kg)
NESCAFE GOLD Refill (170g)
Nestle MILO Activ-Go Chocolate Malt Powder (1kg)
MAGGI Hot Cup - Asam Asam Laksa (60g)
MAGGI 2-Minit Curry (79g x 5 Packs x 2)
MAGGI PAZZTA Cheese Macaroni 70g
.......
29.90
21.90
16.48
1.69
8.50
3.15
5.90
.......
RM40.70
RM26.76
RM21.40
RM1.80
RM9.62
........
9k sold/month
2.3k sold/month
1.8k sold/month
1.7k sold/month
.................
27%
18%
23%
6%
.............
Selangor
Selangor
Selangor
Selangor

Примечание для читателей

OP обратил мое внимание на то, что xpath не работал, как указано в моем ответе. Я проверил сайт еще раз через 2 дня и заметил странное явление. Атрибут class_ класса div действительно изменился. Нашел аналогичный Q . Но это не сильно помогло. Итак, пока я прихожу к выводу, что атрибуты div на веб-сайте shoppee могут снова измениться. Я оставляю эту проблему открытой для решения позже.

Примечание к OP

Ана, приведенный выше код будет работать только для одной страницы, т.е. он будет работать только для веб-страницы, https://shopee.com.my/shop/13377506/search?page=0&sortBy=sales. Я приглашаю вас еще больше повысить свои навыки, решив, как очистить данные для нескольких веб-страниц под тегом продаж. Ваша подсказка - это 1/9 в правом верхнем углу этой страницы и / или ссылки 1 2 3 4 5 внизу страницы. Еще один совет для вас - посмотреть на urljoin в библиотеке urlparse. Надеюсь, это поможет вам начать работу.

Полезные ресурсы

1 голос
/ 08 июня 2020

Страница загружается после того, как первый запрос отправляется на страницу с помощью ajax asyn c, поэтому отправка одного запроса и получение источника нужной страницы кажется невозможной.

Вы должны смоделировать браузер, тогда вы сможете получить исходный код и использовать beautifulsoup. См. Код:

BeautifulSoup way

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from bs4 import BeautifulSoup

driver.get("https://shopee.com.my/shop/13377506/search?page=0&sortBy=sales")
WebDriverWait(driver, 30).until(EC.presence_of_element_located((By.CSS_SELECTOR, '.shop-search-result-view')))

html = driver.page_source
soup = BeautifulSoup(html, 'html.parser')
search = soup.select_one('.shop-search-result-view')
products = search.find_all('a')

for p in products:
    name = p.select('div[data-sqe="name"] > div')[0].get_text()
    price = p.select('div > div:nth-child(2) > div:nth-child(2)')[0].get_text()
    product = p.select('div > div:nth-child(2) > div:nth-child(4)')[0].get_text()
    print('name: ' + name)
    print('price: ' + price)
    print('product: ' + product + '\n')

Однако использование селена - хороший способ получить все, что вы хотите. См. Пример ниже:

Selenium Way

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

driver.get("https://shopee.com.my/shop/13377506/search?page=0&sortBy=sales")
WebDriverWait(driver, 30).until(EC.presence_of_element_located((By.CSS_SELECTOR, '.shop-search-result-view')))

search = driver.find_element_by_css_selector('.shop-search-result-view')
products = search.find_elements_by_css_selector('a')

for p in products:
    name = p.find_element_by_css_selector('div[data-sqe="name"] > div').text
    price = p.find_element_by_css_selector('div > div:nth-child(2) > div:nth-child(2)').text
    product = p.find_element_by_css_selector('div > div:nth-child(2) > div:nth-child(4)').text
    print('name: ' + name)
    print('price: ' + price.replace('\n', ' | '))
    print('product: ' + product + '\n')

0 голосов
/ 28 мая 2020

, пожалуйста, отправьте свой код, чтобы мы могли помочь.

или вы можете начать так ..:)

from bs4 import BeautifulSoup as soup
from urllib.request import urlopen as uReg


my_url = "<url>"
uClient = uReg(my_url)
page_html = uClient.read()
...