Ошибка при очистке вместе с Beautiful Soup и Selenium - PullRequest
0 голосов
/ 27 февраля 2019

Я новичок в Python и в целом.В этом коде я использую как Bs4, так и Selenium.Я использую Selenium, чтобы автоматически нажимать кнопку «Показать больше», чтобы можно было просмотреть все результаты, а не только те, которые были найдены на первой странице показанных результатов.Я пытаюсь очистить следующий веб-сайт: https://boards.euw.leagueoflegends.com/en/search?query=improve

Однако при объединении Bs4 и Selenium 3 поля, которые я собираю (имя пользователя, сервер и тема), теперь дают мне следующие две ошибки.

1) Я получаю AttributeError: у объекта 'NoneType' нет атрибута 'text' для сервера и имени пользователя

Traceback (most recent call last):
  File "failoriginale.py", line 153, in <module>
    main()
  File "failoriginale.py", line 132, in main
    song_data = get_songs(index_page) # Get songs with metadata
  File "failoriginale.py", line 81, in get_songs
    username = row.find(class_='username').text.strip()
AttributeError: 'NoneType' object has no attribute 'text'

2) Я получаю эту ошибку с темой

Traceback (most recent call last):
  File "failoriginale.py", line 153, in <module>
    main()
  File "failoriginale.py", line 132, in main
    song_data = get_songs(index_page) # Get songs with metadata
  File "failoriginale.py", line 86, in get_songs
    topic = row.find('div', {'class':'discussion-footer byline opaque'}).find_all('a')[1].text.strip()
IndexError: list index out of range

Однако, прежде чем объединить bs4 с Selenium, эти 3 поля работали так же, как и другие, поэтому я думаю, что проблема в другом.Я не понимаю, в чем проблема в основной функции с song_data?Я уже посмотрел другие вопросы по stackoverflow, но не смог решить проблему.Я новичок в чистке и bs4, библиотеках селена, извините, если задаю глупый вопрос.

Вот код:

browser = webdriver.Firefox(executable_path='./geckodriver')
browser.get('https://boards.euw.leagueoflegends.com/en/search?query=improve&content_type=discussion')
html = browser.page_source #page_source is where selenium stores the html source

def get_songs(url):

    html = browser.page_source
    index_page = BeautifulSoup(html,'lxml') # Parse the page

    items = index_page.find(id='search-results') # Get the list on from the webpage
    if not items: # If the webpage does not contain the list, we should exit
        print('Something went wrong!', file=sys.stderr)
        sys.exit()
    data = list()
 # button show more, if the page has the show more button, it will click on that x5secs
    if index_page.find('a', {"class": "box show-more",}):
        button = browser.find_element_by_class_name('box.show-more')
        timeout = time.time() + 5
        while True:
            button.click()
            time.sleep(5.25)
            if time.time() > timeout:
                break

html = browser.page_source
    index_page = BeautifulSoup(html,'lxml')
    items = index_page.find(id='search-results')

    for row in items.find_all(class_='discussion-list-item'):

        username = row.find(class_='username').text.strip()
        question = row.find(class_='title-span').text.strip()
        sentence = row.find('span')['title']
        serverzone = row.find(class_='realm').text.strip()
        #print(serverzone)
        topic = row.find('div', {'class':'discussion-footer byline opaque'}).find_all('a')[1].text.strip()
        #print(topic)
        date=row.find(class_='timeago').get('title')
        #print(date)
        views = row.find(class_='view-counts byline').find('span', {'class' : 'number opaque'}).get('data-short-number')
        comments = row.find(class_='num-comments byline').find('span', {'class' : 'number opaque'}).get('data-short-number')

        # Store the data in a dictionary, and add that to our list
        data.append({
                     'username': username,
                     'topic':topic,
                     'question':question,
                     'sentence':sentence,
                     'server':serverzone,
                     'date':date,
                     'number_of_comments':comments,
                     'number_of_views':views
                    })
    return data
def get_song_info(url):
    browser.get(url)
    html2 = browser.page_source
    song_page = BeautifulSoup(html2, features="lxml")
    interesting_html= song_page.find('div', {'class' : 'list'})
    if not interesting_html: # Check if an article tag was found, not all pages have one
        print('No information availible for song at {}'.format(url), file=sys.stderr)
        return {}
    answer = interesting_html.find('span', {'class' : 'high-quality markdown'}).find('p').text.strip() #.find('span', {"class": "high-quality markdown",}).find('p')
    return {'answer': answer} # Return the data of interest



def main():
    index_page = BeautifulSoup(html,'lxml')
    song_data = get_songs(index_page) # Get songs with metadata
     #for each row in the improve page enter the link and extract the data  
    for row in song_data:
        print('Scraping info on {}.'.format(row['link'])) # Might be useful for debugging
        url = row['link'] #defines that the url is the column link in the csv file 
        song_info = get_song_info(url) # Get lyrics and credits for this song, if available
        for key, value in song_info.items():
            row[key] = value # Add the new data to our dictionary
    with open('results.csv', 'w', encoding='utf-8') as f: # Open a csv file for writing
        fieldnames=['link','username','topic','question','sentence','server','date','number_of_comments','number_of_views','answer'] # These are the values we want to store

Спасибо за вашу помощь!

1 Ответ

0 голосов
/ 27 февраля 2019

У меня будет соблазн использовать запросы для получения общего количества результатов и количества результатов в пакете и цикличного нажатия кнопки с условием ожидания, пока все результаты не появятся.Тогда хватай их за один раз, так сказать.Схема ниже, которая может быть переписана по мере необходимости.Вы всегда можете использовать конечную точку n, чтобы перестать нажимать после n страниц и увеличивать n внутри цикла.Вы можете дополнительно добавить WebDriverWait(d,20).until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, '.inline-profile .username'))) первоначально в конце, прежде чем собирать другие элементы, чтобы оставить время после последнего щелчка.

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

data = requests.get('https://boards.euw.leagueoflegends.com/en/search?query=improve&json_wrap=1').json()
total = data['searchResultsCount']
batch = data['resultsCount']

d = webdriver.Chrome()
d.get('https://boards.euw.leagueoflegends.com/en/search?query=improve')

counter = batch
while counter < total:
    WebDriverWait(d, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, '.show-more-label'))).click()
    counter +=batch
    #print(counter)

userNames = [item.text for item in d.find_elements_by_css_selector('.inline-profile .username')]
topics = [item.text for item in d.find_elements_by_css_selector('.inline-profile + a')]
servers = [item.text for item in d.find_elements_by_css_selector('.inline-profile .realm')]
results = list(zip(userNames, topics, servers))

Интересно, что кажется, что оно прекращает обновление до заданного числа конца, несмотря на возможность щелкнутькнопка.Это происходит также при ручном нажатии.

...