2014-12-17 5 views
0

Я довольно новичок в python и beautifulsoup, я написал этот фрагмент кода (прилагается только соответствующая часть). Однако он очень медленный во время выполнения, он занимает около 8 секунд (мне нужно зацикливать его несколько тысяч раз).Ускорение кода (beautifulsoup, python)

Не могли бы вы дать мне какие-либо указания, как сделать это быстрее? Каждая критика приветствуется.

PS. Это может быть актуально: на каждой странице имеется 20 строк, столбцы 0..5 - короткие строки до 100 символов, - 6-й столбец больше, это строка длиной до 2000 символов, request.get (.. .) занимает около 0,2 сек

ReqHTMLContent = bs4.BeautifulSoup(ReqResult.text) 


    ############################################### 
    #print('Adding report ...', flush=True) 

    for TableRow in ReqHTMLContent.select('table#msgTable tr'): 
     #print (TableRow) 

     RpName = TableRow.find_all('td')[0].get_text(strip=True)    
     RpArray[row][0] = RpName 
     #print(RpName) 

     RpCategory = TableRow.find_all('td')[1].get_text(strip=True) 
     RpArray[row][1] = RpCategory 
     #print(RpCategory) 

     RpType = TableRow.find_all('td')[2].get_text(strip=True) 
     RpArray[row][2] = RpType 
     #print(RpType) 

     RpTime = TableRow.find_all('td')[3].get_text(strip=True) 
     RpArray[row][3] = RpTime 
     #print(RpTime) 

     RpTitle = TableRow.find_all('td')[4].get_text(strip=True) 
     RpArray[row][4] = RpTitle 
     #print(RpTitle) 

     #linki i tresc raportu 
     for link in TableRow.find_all("a", attrs={"class": "evLK"}): 
      RpLink = domain_url + link.get('href') 
      RpArray[row][5] = RpLink 
      #print(RpLink) 

      #tresc raportu 
      RpHtml = requests.get(RpLink) 
      RpRaw = bs4.BeautifulSoup(RpHtml.text)   

      #<div id="ctl00_Body_msgDetails1_eventReport" class="ItemA">  
      RpTable = RpRaw.find("div", attrs={"id": "ctl00_Body_msgDetails1_eventReport", "class": "ItemA"})      
      RpText = RpTable.get_text("|", strip=True) 
      RpArray[row][6]=RpText    
      #print(RpText) 


     row += 1    
    ### for TableRow in ReqHTMLContent.select('table#msgTable tr'): 
    ############################################### 
+7

Если этот код работает, только медленно, то вы могли бы быть лучше вы спрашиваете об этом на codereview.stackexchange.com – Blair

+2

Вы вызываете 'TableRow.find_all ('td')' для каждого столбца. На каждый суп по вызову будет найден «td». Поэтому поместите 'TableRow.find_all ('td')' в переменную и повторно используемую переменную, чтобы получить значения каждого столбца. – Netro

+0

Реальная проблема заключается в 'request.get()' для каждой ссылки каждой строки. – Jivan

ответ

2

Вместо использования find_all функции несколько раз BS, попробуйте использовать его раз и навсегда:

RpList = TableRow.find_all('td') 

RpName = RpList[0].get_text(strip=True) 
RpCategory = RpList[1].get_text(strip=True) 
RpType = RpList[2].get_text(strip=True) 
RpTime = RpList[3].get_text(strip=True) 
RpTitle = RpList[4].get_text(strip=True) 

Это не ограничивается этим примером. И, как было предложено, вы можете использовать понимание списка, чтобы сделать это в меньшем количестве строк кода. Но стоимость создания RpName, RpType ... переменных ничто по сравнению со стоимостью вызова функций BS. Поэтому, если это поможет ясности вашего кода, вы можете сохранить его.

В принципе, эта идея состоит в том, чтобы использовать BS как минимум, а Python - максимум.

Кроме того, я думаю, что самая затратная часть вашего кода эта строка:

RpHtml = requests.get(RpLink) 

Что внутри вложенного цикла. Но если вам нужно получить доступ к таким ссылкам, потому что вам нужно что-то, что вы не можете найти в другом месте, я не вижу, как вы его отрежете.

Постарайтесь определить, сколько раз эта линия выполняется, так как вы говорите, что она занимает приблизительно 0,2 секунды. Если это называется, допустим, 40 раз, тогда у вас есть свой ответ.

Сделайте это, если вы хотите, чтобы проверить чистую стоимость requests.get() звонков:

from time import time 
start = time() 
calls = 0 
for link in TableRow.find_all("a", attrs={"class": "evLK"}): 
    RpLink = domain_url + link.get('href') 
    RpArray[row][5] = RpLink 
    calls += 1 
print "get() was called %d times and took %d seconds"%(calls,time()-start) 
+0

Это решение спасло меня 1сек от 8, я собираюсь проверить других – klubow

+0

@klubow. Я думаю, вам не удастся пройти менее 6 или 7 секунд, если вы должны называть 'request.get()' для каждой ссылки в каждой строке. Разве нет другого места, где вы могли бы найти одну и ту же информацию за один выстрел? Или вам абсолютно нужна эта информация? – Jivan

+0

Я пытаюсь получить его непосредственно с веб-страницы admin, я надеюсь, что все будет хорошо. В любом случае, на более позднем этапе мне придется обновить мою базу данных новыми записями, поэтому мне придется использовать этот код. Проблема может быть в запросах для каждого цикла, она называется 20 раз x 0,2 = 4sec – klubow

1

Вы можете изменить это:

RpName = TableRow.find_all('td')[0].get_text(strip=True)    
RpArray[row][0] = RpName 
#print(RpName) 

RpCategory = TableRow.find_all('td')[1].get_text(strip=True) 
RpArray[row][1] = RpCategory 
#print(RpCategory) 

RpType = TableRow.find_all('td')[2].get_text(strip=True) 
RpArray[row][2] = RpType 
#print(RpType) 

RpTime = TableRow.find_all('td')[3].get_text(strip=True) 
RpArray[row][3] = RpTime 
#print(RpTime) 

RpTitle = TableRow.find_all('td')[4].get_text(strip=True) 
RpArray[row][4] = RpTitle 
#print(RpTitle) 

к этому:

RpArray[row] = [td.get_text(strip=True) for td in TableRow.find_all('td')] 

И если вы хотите использовать одно из значений, вы можете сделать это:

RpName = RpArray[row][0] 
+1

Спасибо всем, особенно Jivan, основная проблема заключается в request.get, который занимает 4сек из 8 секунд общего времени. – klubow

1

Присоединение как ответ от Винсент Белтман и Дживан:

RpList = TableRow.find_all('td') 
RpArray[row] = [td.get_text(strip=True) for td in RpList] 

Найти все ТД "только один раз, и цикл по с одним выражением.

2

Помимо других предложений, используйте SoupStrainer для анализа только части документа.

Вот измененный код с другими незначительными исправлениями:

from bs4 import SoupStrainer, BeautifulSoup 
import requests 

# we'll use "div" strainer later 
div = SoupStrainer("div", attrs={"id": "ctl00_Body_msgDetails1_eventReport", "class": "ItemA"}) 

rows = SoupStrainer("table", id="msgTable") 
soup = BeautifulSoup(ReqResult.content, parse_only=rows) 

results = [] 
for row in soup.select('table#msgTable tr'): 
    cells = [td.get_text(strip=True) for td in row.find_all('td')] 

    for link in row.select("a.evLK"): 
     url = domain_url + link.get('href') 
     cells.append(url) 

     inner_soup = BeautifulSoup(requests.get(url).content, parse_only=div) 

     table = inner_soup.find("div", attrs={"id": "ctl00_Body_msgDetails1_eventReport", "class": "ItemA"}) 
     cells.append(table.get_text("|", strip=True)) 

    results.append(cells) 

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

  • Scrapy (фреймворк для веб-соскоб на основе витой)
  • grequests (запросы + GEvent)
+0

Использование Threading может также сделать асинхронный подход. –

+0

'SoupStrainer' действительно ускорил мои поиски, спасибо. – fantabolous

Смежные вопросы