[Python爬虫]利用Selenium等待Ajax加载及模拟自动翻页,爬取东方财富网公司公告

原创 2017年03月12日 16:01:49

1.背景

首先,打开东方财富网公司公告页面(“http://data.eastmoney.com/notices/”)。


单击右键,选择检查“长江电力”处的源代码,如图:


点击右键,查看源代码,查找“长江电力”,并没有在html代码里面找到“长江电力”,而只是在js代码找到。所以,可以判断,该网页采用了Ajax技术,用js动态去加载新的数据。所以,问题一:解决获得通过Ajax动态加载的数据。

然后,点击下一页,网页的url没有变化,其只是用js去加载了新的数据,然后动态改变了Table里面的数据。所以,第二个问题是:模拟翻页,也就是模拟点击网页中的“下一页”按钮。


2.解决方法

1)通过Ajax动态加载的数据

现在的网页越来越多采用了Ajax技术,这样程序便不能确定何时某个元素完全加载出来。这会让元素定位困难而且会提高产生ElementNotVisibleException的概率。

所以,Selenium提供了两种等待方式,一种是隐式等待,一种是显式等待。

隐式等待是等待特定的时间,显示等待是指定某一条件直到这个条件成立时继续执行。在这里,采用显示等待的方式,等待Table中的数据动态加载。

显示等待指定某个条件,然后设置最长等待时间。如果在这个时间还没有找到元素,那么便会抛出异常。

def singlePageCraw(self, driver):
        try:
            element = WebDriverWait(driver, 10).until(
                EC.presence_of_element_located((By.ID, "dt_1"))
            )
            tr_options = element.find_elements_by_tag_name("tr")
            df = pd.DataFrame(columns = ['title', 'content'] )
            list = ['id','name','title','detail','type','time']
            for tr_option in tr_options:
                td_options = tr_option.find_elements_by_tag_name("td")
                res_data = []
                stockid = ''
                filename = ''
                count = 0
                for td_option in td_options:
                    if '更多公告 股吧 研报' in td_option.text:
                        continue
                    df.loc[count] = [list[count], str(td_option.text).strip()]
                    count = count + 1
                    if str.isdigit(str(td_option.text).strip()):
                        stockid = td_option.text
                    if ':' in td_option.text:
                        detail_url = td_option.find_element_by_tag_name("a").get_attribute('href')
                        html_cont = self.downloader.download(detail_url)
                        detail_content = self.parser.parse(detail_url, html_cont)
                        df.loc[count] = [list[count], str(detail_content).strip()]
                        filename = stockid + '_' + detail_url.split(',')[0].split('/')[-1]
                        count = count + 1
                print filename
                df.to_csv("D:/"+ filename +'.csv', index=False)
                
        except Exception,e:
            print str(e)

其中,

element = WebDriverWait(driver, 10).until(
                EC.presence_of_element_located((By.ID, "dt_1"))
            )

便是显示等待。程序默认会每500ms调用一次来查看元素是否已经生成,如果本来元素就是存在的,那么会立即返回。

分析页面结构可知,id为"dt_1"的table元素加载后,数据就会全部加载完毕了。接下来便是根据自己的需要,选择元素,获得元素的属性或者值。

关于元素的选取,有如下的API:

1)单个元素选取

find_element_by_id
find_element_by_name
find_element_by_xpath
find_element_by_link_text
find_element_by_partial_link_text
find_element_by_tag_name
find_element_by_class_name
find_element_by_css_selector

2)多个元素选取

find_elements_by_name
find_elements_by_xpath
find_elements_by_link_text
find_elements_by_partial_link_text
find_elements_by_tag_name
find_elements_by_class_name
find_elements_by_css_selector

关于元素中值的获取:

1)获取元素的值

option.text

2)获取元素的属性值

option.get_attribute('href')

在这里,获取到了动态加载的元素之后,通过选择元素的API,获取元素的值或者属性值。

具体情况具体分析:


可以看到,在table中,每一行中,一共有六列。过滤掉第三列“更多公告 股吧 研报”,对于第四列,不仅要保存标题,还需要保存点击链接后网页中的内容。

在这里,有一个选择,一种方法是同样采用Selenium,打开网页,选择其中的元素来获取内容;另一种方法是使用urllib2下载网页,再用html.parser解析网页提取内容。考虑到打开网页需要的时间较长,而且通过分析,具体公告的内容是直接加载的,所以,采用第二种方法。

下载网页:

# coding:utf8

import urllib2
import socket
import sys

reload(sys)
sys.setdefaultencoding('utf-8')

class HtmlDownloader(object):
      
    def download(self,url):
        socket.setdefaulttimeout(200)
        if url is None:
            return None
        
        response = urllib2.urlopen(url)
        
        if response.getcode() != 200:
            return None
        return unicode(response.read(), 'GB18030', 'ignore').encode('UTF-8')
        #return response.read()


解析网页:

# coding:utf8

from baike_spider import html_downloader
from bs4 import BeautifulSoup
import sys
 
reload(sys)
sys.setdefaultencoding('utf-8')

class HtmlParser(object):
    
    def __init__(self):
        self.downloader = html_downloader.HtmlDownloader()
                   
    def _get_new_data(self, page_url, soup):
        try:   
            #<div class="detail-body" style="padding: 20px 50px;">
            return soup.find('div', class_ = 'detail-body').find('div').get_text()
        except Exception,e:
            print e
    
    def parse(self, page_url, html_cont):
        if page_url is None or html_cont is None:
            return
        
        soup = BeautifulSoup(html_cont, 'html.parser', from_encoding='utf-8')
        return self._get_new_data(page_url, soup)


2)模拟自动翻页

可以看到,点击下一页,网页的url没有发生变化。使用Selenium,模拟点击“下一页”,再使用和前面一样的方法,选择元素,就可以了。

WebDriverWait(driver, 10).until(
                EC.presence_of_element_located((By.XPATH, "//a[contains(text(),'下一页')]"))
            ).click()
            time.sleep(5)


注意:这里可能会出现一个错误:

"org.openqa.selenium.WebDriverException: Element is not clickable at point (411, 675). Other element would receive the click: ..."

具体的原因和解决方法这里有详细的解释:

http://stackoverflow.com/questions/11908249/debugging-element-is-not-clickable-at-point-error

大致分为以下几种:

1.The element is not visible to click.(网页元素不可见)

Use Actions or JavascriptExecutor for making it to click.

By Actions:

WebElement element = driver.findElement(By("element_path"));

Actions actions = new Actions(driver);

actions.moveToElement(element).click().perform();

By JavascriptExecutor:

JavascriptExecutor jse = (JavascriptExecutor)driver;

jse.executeScript("scroll(250, 0)"); // if the element is on top.

jse.executeScript("scroll(0, 250)"); // if the element is on bottom.

or

JavascriptExecutor jse = (JavascriptExecutor)driver;

jse.executeScript("arguments[0].scrollIntoView()", Webelement); 

Then click on the element.

2.The page is getting refreshed before it is clicking the element.(点击元素的时候网页正在刷新)

For this, make the page to wait for few seconds.

3. The element is clickable but there is a spinner/overlay on top of it(网页上有其他元素遮挡了原本要点击的元素)

The below code will wait until the overlay disppears

By loadingImage = By.id("loading image ID");

WebDriverWait wait = new WebDriverWait(driver, timeOutInSeconds);

wait.until(ExpectedConditions.invisibilityOfElementLocated(loadingImage));
结合情况1和情况2,我对上述代码进行了如上处理。


3.小结

这就是爬取东方财富网的全部要点了。这也是我学会的第二项爬虫技能。

在这里,要感谢:

Python爬虫利器五之Selenium的用法

python利用beautifulsoup+selenium自动翻页抓取网页内容

这两篇博文的作者。

完整代码课参考:

Python爬取东方财富网公司公告



版权声明:本文为博主原创文章,未经博主允许不得转载。

Python爬虫小结之Selenium

上学期和同学一块儿接了个爬虫的活儿,赚了人生第一桶金。本来打算寒假好好整理一下,但是,但是寒假整整玩儿了一个月,说好的总结博客没有写,想学的新东西也没有看,真是不思进取啊。现在提前几天回学校,一来准备...
  • MajorDong100
  • MajorDong100
  • 2017年02月19日 19:33
  • 1688

Python-Selenium2做Web自动化测试(11)-下拉框处理、分页处理

一、下拉框处理 下拉框也是 web 页面上非常常见的功能,要想定位下拉框中的内容,首先需要定位到下拉框;引用网友的Html文件 drop_down.html ...
  • jasonwoolf
  • jasonwoolf
  • 2015年08月09日 18:48
  • 3118

python下利用Selenium获取动态页面数据

利用python爬取网站数据非常便捷,效率非常高,但是常用的一般都是使用BeautifSoup、requests搭配组合抓取静态页面,即网页上显示的数据都可以在html源码中找到,而不是网站通过js或...
  • qq_23926575
  • qq_23926575
  • 2017年05月20日 11:24
  • 6787

RSelenium包抓取链家网(上:模拟点击与页面抓取)

安装RSelenium包install.packages("RSelenium") # 直接从CRAN下载RSelenium包启动Selenium服务器 在控制台输入java -jar D:\R\li...
  • Joyliness
  • Joyliness
  • 2017年12月14日 19:21
  • 439

Python+Selenium 自动化实现实例-处理分页(pagination)

场景 对分页来说,我们最感兴趣的是下面几个信息 总共有多少页当前是第几页是否可以上一页和下一页 代码 下面代码演示如何获取分页总数及当前页数、跳转到指定页数 #coding:utf-8 f...
  • a136332462
  • a136332462
  • 2017年06月09日 13:39
  • 615

selenium-webdriver 使用WebDriverWait显示等待ajax交互

使用selenium版本为2.44.0 现在很多网站都是使用ajax进行交互,常见就是某个操作后,等待结果展示,但是不会重新加载页面,这个时候不能使用原始的等待方法: webDriver.mana...
  • xian312854159
  • xian312854159
  • 2015年02月10日 15:19
  • 4628

使用Selenium来抓取动态加载的页面

原文:http://my.oschina.net/flashsword/blog/147334?p=1 一般的爬虫都是直接使用http协议,下载指定url的html内容,并对内容进行分析和抽取。...
  • jianzhanger
  • jianzhanger
  • 2015年04月02日 15:31
  • 11501

Selenium+PhantomJS加载ajax数据

如今的网站有两种。一种是同步加载的。另一种是异步加载的,也即我们常说的用ajax。对于那种同步加载的网站,普通的爬虫程序轻轻松松的就能搞定。但是对于那种异步请求数据的网站,就不能走寻常路了。对于这种情...
  • it_roseonly
  • it_roseonly
  • 2017年06月26日 15:17
  • 858

使用python+selenium制作浏览器爬虫,彻底解决ajax异步加载问题(待更新)

打开浏览器需要下载相应的webdriver并保存到系统path下。chrome对应的webdriver下载地址:http://download.csdn.net/detail/u013760453/9...
  • u013760453
  • u013760453
  • 2017年03月23日 17:25
  • 1682

Selenium之Web页面滚动条滚操作

//移动到元素element对象的“顶端”与当前窗口的“顶部”对齐 ((JavascriptExecutor) driver).executeScript("arguments[0].scrollI...
  • jlminghui
  • jlminghui
  • 2016年01月07日 17:55
  • 16151
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:[Python爬虫]利用Selenium等待Ajax加载及模拟自动翻页,爬取东方财富网公司公告
举报原因:
原因补充:

(最多只允许输入30个字)