Как сделать так, чтобы веб-драйвер phantomJS дождался загрузки определенного HTML-элемента, а затем возвратил page.source? - PullRequest
0 голосов
/ 05 апреля 2019

Я разработал приведенный ниже код для объекта сканирования в Интернете.

Он принимает две даты в качестве входных данных. Затем создается список дат между этими двумя датами и прикрепляется каждая к URL-адресу веб-страницы, который содержит информацию о погоде.местоположения.Затем он преобразует HTML-таблицы данных в Dataframe, а затем сохраняет данные в виде CSV-файла в хранилище (базовая ссылка: https://www.wunderground.com/history/daily/ir/mashhad/OIMM/date/2019-1-3, и, как вы можете видеть в этом примере, дата 2019-1-3):

from datetime import timedelta, date
from bs4 import BeautifulSoup
from selenium import webdriver
import pandas as pd
from furl import furl
import os
import time

class WebCrawler():
    def __init__(self, st_date, end_date):
        if not os.path.exists('Data'):
            os.makedirs('Data')
        self.path = os.path.join(os.getcwd(), 'Data')
        self.driver = webdriver.PhantomJS()
        self.base_url = 'https://www.wunderground.com/history/daily/ir/mashhad/OIMM/date/'
        self.st_date = st_date
        self.end_date = end_date

    def date_list(self):
        # Create list of dates between two dates given as inputs.
        dates = []
        total_days = int((self.end_date - self.st_date).days + 1)

        for i in range(total_days):
            date = self.st_date + timedelta(days=i)
            dates.append(date.strftime('%Y-%m-%d'))

        return dates

    def create_link(self, attachment):
        # Attach dates to base link
        f = furl(self.base_url)
        f.path /= attachment
        f.path.normalize()

        return f.url

    def open_link(self, link):
        # Opens link and visits page and returns html source code of page
        self.driver.get(link)
        html = self.driver.page_source

        return html

    def table_to_df(self, html):
        # Finds table of weather data and converts it into pandas dataframe and returns it
        soup = BeautifulSoup(html, 'lxml')
        table = soup.find("table",{"class":"tablesaw-sortable"})

        dfs = pd.read_html(str(table))
        df = dfs[0]

        return df

    def to_csv(self, name, df):
        # Save the dataframe as csv file in the defined path
        filename = name + '.csv'
        df.to_csv(os.path.join(self.path,filename), index=False)

Вот как я хочу использовать объект WebCrawler:

date1 = date(2018, 12, 29)
date2 = date(2019, 1, 1)

# Initialize WebCrawler object
crawler = WebCrawler(st_date=date1, end_date=date2)
dates = crawler.date_list()

for day in dates:
    print('**************************')
    print('PROCESSING : ', day)
    link = crawler.create_link(day)
    print('WAITING... ')
    time.sleep(3)
    print('VISIT WEBPAGE ... ')
    html = crawler.open_link(link)
    print('DATA RETRIEVED ... ')
    df = crawler.table_to_df(html)
    print(df.head(3))
    crawler.to_csv(day, df)
    print('DATA SAVED ...')

Проблема, которая возникает, состоит в том, что первая итерация цикла выполняется идеально, а вторая останавливается сошибка, которая говорит No tables where found (встречается в строке table = soup.find("table",{"class":"tablesaw-sortable"})), и это потому, что источник страницы возвращается WebCrawler.open_link до того, как веб-страница полностью загрузит содержимое веб-страницы, включая таблицу (содержащую информацию о погоде).также существует вероятность того, что веб-сайт отклонит запрос, потому что это делает серверы слишком загруженными.

В любом случае можно ли создать цикл, который будет продолжать пытаться открыть ссылку до тех пор, пока он не сможет найти таблицу, или вподождать, пока таблица загрузится, а затем вернуть таблицу?

Ответы [ 2 ]

1 голос
/ 05 апреля 2019

Селен может ждать определенного элемента.В вашем случае это будет таблица с именем класса «tableaw-sortable».Я очень рекомендую использовать CSS-селекторы, чтобы найти этот элемент, поскольку он быстрее и менее подвержен ошибкам при получении всех элементов таблицы.

Вот селектор CSS, подготовленный для вас table.tablesaw-sortable,Установите селен, чтобы дождаться загрузки этого элемента.

Источник: https://stackoverflow.com/a/26567563/4159473

0 голосов
/ 05 апреля 2019

Я переписал код, используя решение https://stackoverflow.com/a/26567563/4159473, предложенное @mildmelon, и также использовал некоторые задержки между каждой отправкой запроса на сервер и запросом источника страницы:

from datetime import timedelta, date
from bs4 import BeautifulSoup
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
from selenium.common.exceptions import TimeoutException
import pandas as pd
from furl import furl
import os
import time
class WebCrawler():
    def __init__(self, st_date, end_date):
        if not os.path.exists('Data'):
            os.makedirs('Data')
        self.path = os.path.join(os.getcwd(), 'Data')
        self.driver = webdriver.PhantomJS()
        self.delay_for_page = 7
        self.base_url = 'https://www.wunderground.com/history/daily/ir/mashhad/OIMM/date/'
        self.st_date = st_date
        self.end_date = end_date

    def date_list(self):
        # Create list of dates between two dates given as inputs.
        dates = []
        total_days = int((self.end_date - self.st_date).days + 1)

        for i in range(total_days):
            date = self.st_date + timedelta(days=i)
            dates.append(date.strftime('%Y-%m-%d'))

        return dates

    def create_link(self, attachment):
        # Attach dates to base link
        f = furl(self.base_url)
        f.path /= attachment
        f.path.normalize()

        return f.url

    def open_link(self, link):
        # Opens link and visits page and returns html source code of page
        self.driver.get(link)
        myElem = WebDriverWait(self.driver, self.delay_for_page)\
        .until(EC.presence_of_element_located((By.CLASS_NAME, 'tablesaw-sortable')))


    def table_to_df(self, html):
        # Finds table of weather data and converts it into pandas dataframe and returns it
        soup = BeautifulSoup(html, 'lxml')
        table = soup.find("table",{"class":"tablesaw-sortable"})

        dfs = pd.read_html(str(table))
        df = dfs[0]

        return df

    def to_csv(self, name, df):
        # Save the dataframe as csv file in the defined path
        filename = name + '.csv'
        df.to_csv(os.path.join(self.path,filename), index=False)

date1 = date(2019, 2, 1)
date2 = date(2019, 3, 5)


# Initialize WebCrawler object
crawler = WebCrawler(st_date=date1, end_date=date2)
dates = crawler.date_list()
for day in few_dates:
    print('**************************')
    print('DATE : ', day)
    link = crawler.create_link(day)
    print('WAITING ....')
    print('')
    time.sleep(12)
    print('OPENING LINK ... ')

    try:
        crawler.open_link(link)
        html = crawler.driver.page_source
        print( "DATA IS FETCHED")
        df = crawler.table_to_df(html)
        print(df.head(3))
        crawler.to_csv(day, df)
        print('DATA SAVED ...')
    except TimeoutException:
        print( "NOT FETCHED ...!!!")

Информация о погоде выбирается без проблем. Я предполагаю, что задержки между каждым запросом привели к лучшей производительности. Линия myElem = WebDriverWait(self.driver, self.delay_for_page)\.until(EC.presence_of_element_located((By.CLASS_NAME, 'tablesaw-sortable'))) также улучшила скорость.

...