Красивый суп — для цикла, не перебирающего все теги в td

#python #iframe #web-scraping #beautifulsoup

#python #iframe #очистка веб-страниц #beautifulsoup

Вопрос:

Я пытаюсь очистить некоторые данные с веб-сайта, используя BeautifulSoup. Я могу выбрать тег td, но он не содержит всех дочерних тегов, которые я ожидал бы. Моя цель — выполнить итерацию по тегу td с идентификатором =»highlight_today» и получить все сегодняшние события. URL, который я пытаюсь очистить, являетсяhttp://b-us.econoday.com/byweek.asp?containerId=eco-iframe-container . Это iframe на другой странице,http://www.bloomberg.com/markets/economic-calendar . Я думаю, что другой iframe может быть причиной того, что мой цикл for не работает, и я не извлекаю все теги, которые я ожидал бы в этом td. Мой опыт работы с html очень ограничен, поэтому я не уверен. Мой код выглядит следующим образом:

 import re
import requests
from bs4 import BeautifulSoup
import time

url_to_scrape = 'http://b-us.econoday.com/byweek.asp?containerId=eco-iframe-container'
r = requests.get(url_to_scrape)
html = r.content
soup = BeautifulSoup(html, "html.parser")

for events in soup.find('td', {'id': "highlight_today"}):
     print events.text  
  

Я ожидаю получить все теги, содержащиеся в td, но в конечном итоге он останавливается на этом элементе в html-коде и не переходит к следующему div в td:

  <span class="econoarticles"><a href="byshoweventfull.asp?fid=476382amp;amp;cust=b-usamp;amp;year=2016amp;amp;lid=0amp;amp;containerId=eco-iframe-containeramp;amp;prev=/byweek.asp#top">Daniel Tarullo Speaks<br></a></span>
  

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

Ответ №1:

soup.find() возвращает один тег. Возможно, вы имели в виду использовать find_all() ?

Кроме того, почему вы ожидаете найти более одного элемента с заданным идентификатором? Идентификаторы HTML (должны быть) уникальными для всего документа.

Ответ №2:

Попробуйте этот код. Используйте веб-драйвер Selenium.

 from selenium import webdriver
import time
import datetime
import csv
import re
from datetime import timedelta
import pandas as pd #use pandas dataframe to store downloaded data
from StringIO import StringIO

driver = webdriver.Firefox()  # Optional argument, if not specified will search path.
driver.get("http://b-us.econoday.com/byweek.asp?containerId=eco-iframe-container")
time.sleep(5)
table=driver.find_element_by_xpath("html/body/table/tbody/tr[4]/td/table[1]/tbody/tr/td/table[2]/tbody/tr[2]")
#table=driver.find_element_by_class_name('eventstable')
columns=table.find_elements_by_tag_name('td')
time.sleep(1)
#option 1 get the hole column
for col in columns:
    print(col.text)
#option 2 get info row by row, but information is hided in to different classes
for col in columns_list:
    rows=col.find_elements_by_tag_name('div')
    for row in rows:
        print(row.text)
    rows=col.find_elements_by_tag_name('span')
    for row in rows:
        print(row.text)
  

Результат для последней колонки новостей будет:

 Market Focus »


Daniel Tarullo Speaks
10:15 AM ET

Baker-Hughes Rig Count
1:00 PM ET

John Williams Speaks
2:30 PM ET
  

Вы можете проанализировать эти строки. Используйте разные имена классов для поиска необходимой информации.

Комментарии:

1. Зачем вам selenium?

2. Автор вопроса запросил все предложения для достижения своей цели. Обычно я использую Selenium, потому что он обеспечивает больше функциональности (щелчок по веб-элементам в случае интерактивной веб-страницы и так далее). Иногда лучше использовать более общий подход даже для простых задач, если вы его хорошо знаете.

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

4. Я подумал, что это интересно. Я, вероятно, также не стал бы использовать Selenium в этом случае, но это не означает, что подобный ответ должен быть немедленно удален.

Ответ №3:

Есть один td с идентификатором highlight_today, все дочерние элементы содержатся в теге, поэтому вы просто нажимаете на него, и если вы хотите выполнить итерацию по дочерним элементам, вы бы вызвали find_all:

 import requests
from bs4 import BeautifulSoup


url_to_scrape = 'http://b-us.econoday.com/byweek.asp?containerId=eco-iframe-container'
r = requests.get(url_to_scrape)
html = r.content
soup = BeautifulSoup(html, "html.parser")
event = soup.find('td', {'id': "highlight_today"})
for tag in event.find_all():
    print(tag)
  

Что дало бы вам:

 <div class="econoitems"><br/><span class="econoitems"><a href="byshoweventfull.asp?fid=476407amp;amp;cust=b-usamp;amp;year=2016amp;amp;lid=0amp;amp;containerId=eco-iframe-containeramp;amp;prev=/byweek.asp#top">Market Focus <span class="econo-item-arrow">»</span></a></span><br/></div>
<br/>
<span class="econoitems"><a href="byshoweventfull.asp?fid=476407amp;amp;cust=b-usamp;amp;year=2016amp;amp;lid=0amp;amp;containerId=eco-iframe-containeramp;amp;prev=/byweek.asp#top">Market Focus <span class="econo-item-arrow">»</span></a></span>
<a href="byshoweventfull.asp?fid=476407amp;amp;cust=b-usamp;amp;year=2016amp;amp;lid=0amp;amp;containerId=eco-iframe-containeramp;amp;prev=/byweek.asp#top">Market Focus <span class="econo-item-arrow">»</span></a>
<span class="econo-item-arrow">»</span>
<br/>
<br/>
<div class="itembreak"></div>
<br/>
<span class="econoarticles"><a href="byshoweventfull.asp?fid=476382amp;amp;cust=b-usamp;amp;year=2016amp;amp;lid=0amp;amp;containerId=eco-iframe-containeramp;amp;prev=/byweek.asp#top">Daniel Tarullo Speaks<br/></a></span>
<a href="byshoweventfull.asp?fid=476382amp;amp;cust=b-usamp;amp;year=2016amp;amp;lid=0amp;amp;containerId=eco-iframe-containeramp;amp;prev=/byweek.asp#top">Daniel Tarullo Speaks<br/></a>
<br/>
  

HTML на самом деле сломан, поэтому вам понадобится либо lxml, либо html5lib для синтаксического анализа i. Затем, чтобы получить то, что вы хотите, вам нужно найти промежутки с econoarticles классом и проделать немного дополнительной работы, чтобы получить времена:

  url_to_scrape = 'http://b-us.econoday.com/byweek.asp?containerId=eco-iframe-container'
r = requests.get(url_to_scrape)
html = r.content
soup = BeautifulSoup(html, "lxml")
event = soup.find('td', {'id': "highlight_today"})
for span in event.select("span.econoarticles"):
    speaker, time, a = span.text, span.find_next_sibling(text=True), span.a["href"]
    print(speaker, time, a)
  

Который, если мы запустим, даст вам:

 In [2]: import requests
   ...: from bs4 import BeautifulSoup
   ...: url_to_scrape = 'http://b-us.econoday.com/byweek.asp?containerId=eco-ifr
   ...: ame-container'
   ...: r = requests.get(url_to_scrape)
   ...: html = r.content
   ...: soup = BeautifulSoup(html, "lxml")
   ...: event = soup.find('td', {'id': "highlight_today"})
   ...: for span in event.select("span.econoarticles"):
   ...:     speaker, time, a = span.text, span.find_next_sibling(text=True), spa
   ...: n.a["href"]
   ...:     print(speaker, time, a)
   ...: 

Daniel Tarullo Speaks 10:15 AM ET byshoweventfull.asp?fid=476382amp;cust=b-usamp;year=2016amp;lid=0amp;containerId=eco-iframe-containeramp;prev=/byweek.asp#top
John Williams Speaks 2:30 PM ET byshoweventfull.asp?fid=476390amp;cust=b-usamp;year=2016amp;lid=0amp;containerId=eco-iframe-containeramp;prev=/byweek.asp#top

In [3]: 
  

если вам нужны Market Focus и URL для этого, просто добавьте:

 event = soup.find('td', {'id': "highlight_today"})
det = event.select_one("span.econoitems")
name, an = det.text, det.a["href"]
print(name, an )
  

Комментарии:

1. Спасибо @PadraicCunningham, это идеально подходит для моих целей. После попыток установить lxml (наконец-то разобрался). Это прекрасно работает. Просто понадобилась одна небольшая правка, чтобы я мог получить все, что искал: for span in event.select("span.econoarticles, div.econoevents"):