从使用 python 对象呈现 url 的 javascript 解析 html

Parsing html from a javascript rendered url with python object

我想从以下 url 及其所有后续页面中提取市场信息:

https://uk.reuters.com/investing/markets/index/.FTSE?sortBy=&sortDir=&pn=1

我已经使用以下 url:

中的一些代码成功地从第一页解析了我想要的数据
https://impythonist.wordpress.com/2015/01/06/ultimate-guide-for-scraping-javascript-rendered-web-pages

我还能够解析出下一页的 url 以输入循环以便从下一页获取数据。问题是由于我不完全理解的原因,它在加载下一页之前崩溃了。

我有一种预感,我从 'impythonist' 那里借来的 class 可能是导致问题的原因。我不知道足够的面向对象编程来解决这个问题。这是我的代码,其中大部分是从上面的 url 借来的:

import sys  
from PyQt4.QtGui import *  
from PyQt4.QtCore import *  
from PyQt4.QtWebKit import *  
from lxml import html
import re
from bs4 import BeautifulSoup

class Render(QWebPage):  
  def __init__(self, url):  
    self.app = QApplication(sys.argv)  
    QWebPage.__init__(self)  
    self.loadFinished.connect(self._loadFinished)  
    self.mainFrame().load(QUrl(url))  
    self.app.exec_()  

  def _loadFinished(self, result):  
    self.frame = self.mainFrame()  
    self.app.quit()  



base_url='https://uk.reuters.com'
complete_next_page='https://uk.reuters.com/investing/markets/index/.FTSE?sortBy=&sortDir=&pn=1'

#LOOP TO RENDER PAGES AND GRAB DATA
while complete_next_page != '':
    print ('NEXT PAGE: ',complete_next_page, '\n')
    r = Render(complete_next_page)  # USE THE CLASS TO RENDER JAVASCRIPT FROM PAGE
    result = r.frame.toHtml()     # ERROR IS THROWN HERE ON 2nd PAGE

# PARSE THE HTML
soup = BeautifulSoup(result, 'lxml')
row_data=soup.find('div', attrs={'class':'column1 gridPanel grid8'})
print (len(row_data))

# PARSE ALL ROW DATA
stripe_rows=row_data.findAll('tr', attrs={'class':'stripe'})
non_stripe_rows=row_data.findAll('tr', attrs={'class':''})
print (len(stripe_rows))
print (len(non_stripe_rows))

# PARSE SPECIFIC ROW DATA FROM INDEX COMPONENTS
#non_stripe_rows: from 4 to 18 (inclusive) contain data
#stripe_rows: from 2 to 16 (inclusive) contain data
i=2
while i < len(stripe_rows):
    print('CURRENT LINE IS: ',str(i))
    print(stripe_rows[i])
    print('###############################################')
    print(non_stripe_rows[i+2])
    print('\n')
    i+=1

#GETS LINK TO NEXT PAGE
next_page=str(soup.find('div', attrs={'class':'pageNavigation'}).find('li', attrs={'class':'next'}).find('a')['href']) #GETS LINK TO NEXT PAGE WORKS
complete_next_page=base_url+next_page

我已经注释了我编写和理解的代码位,但我真的不知道 'Render' class 中发生了什么足以诊断错误?除非是别的什么?

这里是错误:

result = r.frame.toHtml()
AttributeError: 'Render' object has no attribute 'frame'

一旦我解析了它,我就不需要将信息保留在 class 中,所以我在想也许它可以以某种方式被清除或重置,然后更新以保存新的 url 来自 2:n 页的信息,但我不知道该怎么做?

或者,如果有人知道从该页面和后续页面获取此特定数据的另一种方法,那同样有用吗?

非常感谢。

用 selenium 和 phantomjs 代替 PyQt 怎么样?
您可以通过执行 "pip install selenium" 轻松获取硒。 如果你使用 Mac 你可以通过执行 "brew install phantomjs" 来获得 phantomjs。 如果您的 PC Windows 使用 choco 而不是 brew,或者 Ubuntu 使用 apt-get。

from selenium import webdriver
from bs4 import BeautifulSoup

base_url = "https://uk.reuters.com"
first_page = "/business/markets/index/.FTSE?sortBy=&sortDir=&pn=1"

browser = webdriver.PhantomJS()

# PARSE THE HTML
browser.get(base_url + first_page)
soup = BeautifulSoup(browser.page_source, "lxml")
row_data = soup.find('div', attrs={'class':'column1 gridPanel grid8'})

# PARSE ALL ROW DATA
stripe_rows = row_data.findAll('tr', attrs={'class':'stripe'})
non_stripe_rows = row_data.findAll('tr', attrs={'class':''})
print(len(stripe_rows), len(non_stripe_rows))

# GO TO THE NEXT PAGE
next_button = soup.find("li", attrs={"class":"next"})
while next_button:
  next_page = next_button.find("a")["href"]
  browser.get(base_url + next_page)
  soup = BeautifulSoup(browser.page_source, "lxml")
  row_data = soup.find('div', attrs={'class':'column1 gridPanel grid8'})
  stripe_rows = row_data.findAll('tr', attrs={'class':'stripe'})
  non_stripe_rows = row_data.findAll('tr', attrs={'class':''})
  print(len(stripe_rows), len(non_stripe_rows))
  next_button = soup.find("li", attrs={"class":"next"})

# DONT FORGET THIS!!
browser.quit()

我知道上面的代码效率不高(我觉得太慢了),但我认为它会给你带来你想要的结果。另外,如果你要抓取的网页没有使用Javascript,连PhantomJS和selenium都不需要。您可以使用请求模块。但是,因为我想向您展示与 PyQt 的对比,所以我在这个答案中使用了 PhantomJS 和 Selenium。