python抓取动态网页(有没有什么办法可以直接获取网页的动态渲染数据呢? )

优采云 发布时间: 2021-11-14 11:02

  python抓取动态网页(有没有什么办法可以直接获取网页的动态渲染数据呢?

)

  成功完成基金净值爬虫的爬虫后,在简单了解了爬虫的一些原理后,不禁有些疑惑——为什么不能通过Request直接获取网页的源码,但是通过查找相关的js文件来爬取数据??

  有时我们使用requests抓取页面时,得到的结果可能与浏览器中看到的不同:在浏览器中可以看到正常显示的页面数据,但是使用requests获取的结果却看不到。

  这是因为获取的请求都是原创的 HTML 文档,浏览器中的页面是通过 JavaScript 处理数据生成的结果。这些数据来自各种来源,可能通过 Ajax 加载或收录在 HTML 文档中。, 也可能是经过 JavaScript 和特定算法计算后生成的。按照目前Web发展的趋势,网页的原创HTML文档不收录任何数据。通过Ajax等方式统一加载后显示,从而在Web开发中实现前后端分离,减少服务器直接渲染页面。带来。通常,我们称这种网页为动态渲染页面。

  之前的基金净值数据爬虫采用的是直接访问服务器获取数据接口,即找到收录数据的js文件,向服务器发送相关请求获取文件。

  那么,有没有什么办法可以直接获取网页的动态渲染数据呢?答案是肯定的。

  我们也可以直接用模拟浏览器操作的方式来实现动态网页的爬取,这样我们就可以在浏览器中看到它的样子,爬取到的源代码是什么,也就是实现了——可见然后爬行。

  Python提供了很多模拟浏览器操作的库,如:Selenium、Splash、PyV8、Ghost等,本文继续以基金净值爬虫为例,使用Selenium对其动态页面进行爬取。

  环境工具

  1、Chrome 及其开发者工具

  2、蟒蛇3.7

  3、PyCharm

  python中使用的库3.7

  1、硒

  2、熊猫

  3、随机

  4、 时间

  5、操作系统

  系统

  Mac OS 10.13.2

  Selenium 基本功能和用法

  准备好工作了

  ChromDriver 配置

  基本使用

  首先,我们先来了解一下Selenium的一些特性以及它能做什么:

  Selenium 是一种自动化测试工具,可以驱动浏览器执行特定的动作,例如点击、下拉等操作。同时,它还可以获取浏览器当前呈现的页面的源代码,以便在可见时进行抓取。对于一些动态渲染的页面,这种爬取方式非常有效。它的基本功能也非常方便。下面我们来看一些简单的代码:

   1 from selenium import webdriver

2 from selenium.webdriver.common.by import By

3 from selenium.webdriver.common.keys import Keys

4 from selenium.webdriver.support import expected_conditions as EC

5 from selenium.webdriver.support.wait import WebDriverWait

6

7

8 browser = webdriver.Chrome() # 声明浏览器对象

9 try:

10 browser.get(\'https://www.baidu.com\') # 传入链接URL请求网页

11 query = browser.find_element_by_id(\'kw\') # 查找节点

12 query.send_keys(\'Python\') # 输入文字

13 query.send_keys(Keys.ENTER) # 回车跳转页面

14 wait = WebDriverWait(browser, 10) # 设置最长加载等待时间

15 print(browser.current_url) # 获取当前URL

16 print(browser.get_cookies()) # 获取当前Cookies

17 print(browser.page_source) # 获取源代码

18 finally:

19 browser.close() # 关闭浏览器

  运行代码后,Chrome 浏览器会自动弹出。浏览器会跳转到百度,然后在搜索框中输入Python→回车→跳转到搜索结果页面,得到结果后关闭浏览器。这相当于模拟了我们在百度上搜索Python的全套动作,给你带来惊喜!!

  

  在这个过程中,当加载网页结果时,控制台会分别输出当前的URL、当前的Cookies和网页源代码:

  

  可以看出,我们得到的内容是浏览器中的真实内容。可见,使用Selenium驱动浏览器加载网页,可以直接得到JavaScript渲染的结果。接下来我们也会主要用Selenium来爬取基金的净值~

  注:Selenium更详细的用法和功能可以在官网查看()

  基金净值数据爬虫

  通过前面的爬虫,我们会发*敏*感*词*净值数据爬虫。

  页面分析

  这个爬虫的目标是单个基金的净值数据。抓取到的网址为:(以单个基金519961为例)。URL 的结构是显而易见的。当我们在浏览器中输入访问链接时,会显示最新的。基金权益数据第一页结果:

  

  在数据下方,有一个页面导航,其中包括前五页之间的链接以及上一页和下一页之间的链接。还有一个链接可以输入任何要跳转到的页码:

  

  如果我们想要获取第二页及以后的数据,我们需要跳转到相应的页码。因此,如果我们需要获取所有的历史净值数据,只需要遍历所有的页码即可。可以直接在页面跳转文本框中输入要跳转到的页码,然后点击“确定”按钮跳转到对应页码的页面。

  这里没有直接点击“下一页”的原因是:一旦在爬取过程中出现异常退出,比如在第50页退出时,此时点击“下一页”就无法快速切换到当前页面相应的后续页面。另外,在爬取过程中需要记录当前爬虫的进度,以便及时进行异常检测,检测问题在第一页。整个过程比较复杂,采用直接跳转的方式抓取网页更合理。

  当我们成功加载了某个页面的净值数据后,我们就可以使用Selenium来获取该页面的源代码了。定位到一个特定的节点后,我们就可以得到目标的HTML内容,然后通过相应的分析就可以得到我们的HTML内容。目标数据。下面,我们用代码来实现整个爬取过程。

  获取基金权益清单

  首先,我们需要构造目标 URL。这里的URL组成规则很明显,就是基金code.html。我们可以使用规则来构造我们要爬取的基金对象。这里以基金519961为例进行爬取。

   1 browser = webdriver.Chrome()

2 wait = WebDriverWait(browser, 10)

3 fundcode=\'519961\'

4

5 def index_page(page):

6 \'\'\'

7 抓取基金索引页

8 :param page: 页码

9 :param fundcode: 基金代码

10 \'\'\'

11 print(\'正在爬取基金%s第%d页\' % (fundcode, page))

12 try:

13 url = \'http://fundf10.eastmoney.com/jjjz_%s.html\' % fundcode

14 browser.get(url)

15 if page>1:

16 input_page = wait.until(

17 EC.presence_of_element_located((By.CSS_SELECTOR, \'#pagebar input.pnum\')))

18 submit = wait.until(

19 EC.element_to_be_clickable((By.CSS_SELECTOR, \'#pagebar input.pgo\')))

20 input_page.clear()

21 input_page.send_keys(str(page))

22 submit.click()

23 wait.until(

24 EC.text_to_be_present_in_element((By.CSS_SELECTOR, \'#pagebar label.cur\'),

25 str(page)))

26 wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, \'#jztable\')))

27 get_jjjz()

28 except TimeoutException:

29 index_page(page)

  这里首相构造了一个WebDriver对象,即声明浏览器对象,使用的浏览器是Chrome,然后指定一个基金代码(519961),然后定义index_page()方法来抓取列表基金净值数据。

  在这种方法中,我们首先访问链接搜索资金,然后判断当前页码。如果大于1,我们会跳转到页面,否则我们将等待页面加载。

  在等待加载时,我们使用WebDriverWait对象,它可以指定等待条件,同时制定一个最大等待时间,这里指定为最长10秒。如果在这段时间内满足等待条件,即页面元素加载成功,则立即返回相应结果并继续向下执行,否则,当最大等待时间未加载时,直接抛出一个超市例外。

  比如我们最后需要等待历史权益信息加载完毕并指定presence_of_element_located条件,然后传入CSS选择器对应的条件#jztable,这个选择器对应的页面内容就是每个基金权益数据页面。在网页上查看:

  

  注意:这里有一个小技巧。如果同学们对CSS选择器的语法不熟悉,可以右键选中节点→复制→复制选择器,直接获取对应的选择器:

  

  CSS 选择器的语法请参考 CSS 选择器参考手册 ()。

  加载成功后,机会订单的后续get_jjjz()方法提取历史净值信息。

  关于翻页操作,这里先获取页码输入框,赋值给input_page,然后获取“OK”按钮,赋值给submit:

  

  首先,我们需要在输入框中调用clear()方法(不管输入框是否有页码数据)。然后,调用send_keys()方法将页码填入输入框,然后点击“确定”按钮,听起来和我们正常的操作方法一样。

  那么,如何知道是否跳转到了对应的页码呢?我们可以注意到,当跳转到当前页面时,页码会高亮显示:

  

  我们只需要判断当前高亮的页码就是当前页码,这里左移使用了另一个等待条件text_to_be_present_in_element,它会等待指定的文本出现在某个节点然后返回成功,这里我们就高亮了亮页码和当前要跳转到的页码对应的CSS选择器通过参数传递给这个等待条件,这样它就会检查当前高亮页码节点是否是我们传递过来的页码数,如果是即,证明页面成功跳转到此页面,页面跳转成功。

  这样,刚刚实现的index_page()方法就可以传入对应的页码,加载完页码对应的商品列表后,调用get_jjjz()方法进行页面分析。

  解析历史净值数据列表

  接下来,我们可以实现 get_jjjz() 方法来解析历史净值数据列表。在这里,我们通过搜索所有历史权益数据节点来获取对应的HTML内容

  

  并进行相应的分析,实现如下:

   1 def get_jjjz():

2 \'\'\'

3 提取基金净值数据

4 \'\'\'

5 lsjz = pd.DataFrame()

6 html_list = browser.find_elements_by_css_selector(\'#jztable tbody tr\')

7 for html in html_list:

8 data = html.text.split(\' \')

9 datas = {

10 \'净值日期\': data[0],

11 \'单位净值\': data[1],

12 \'累计净值\': data[2],

13 \'日增长率\': data[3],

14 \'申购状态\': data[4],

15 \'赎回状态\': data[5],

16 }

17 lsjz = lsjz.append(datas, ignore_index=True)

18 save_to_csv(lsjz)

  首先调用 find_elements_by_css_selector 获取所有存储历史权益数据的节点。此时使用的CSS选择器是#jztable tbody tr,会匹配所有基金权益节点,输出是一个打包成列表的HTML。使用for循环遍历列表,使用text方法提取每个html中的文本内容,得到的输出是空格分隔的字符串数据。为了方便后续处理,我们可以使用split方法将数据拆分成一个新的以列表形式存储,再转换为dict形式。

  最后,为了方便处理,我们将遍历的数据存储为DataFrame,然后使用save_to_csv()方法存储为csv文件。

  另存为本地 csv 文件

  接下来,我们将获取到的基金历史股权数据保存为本地csv文件。实现代码如下:

   1 def save_to_csv(lsjz):

2 \'\'\'

3 保存为csv文件

4 : param result: 历史净值

5 \'\'\'

6 file_path = \'lsjz_%s.csv\' % fundcode

7 try:

8 if not os.path.isfile(file_path): # 判断当前目录下是否已存在该csv文件,若不存在,则直接存储

9 lsjz.to_csv(file_path, index=False)

10 else: # 若已存在,则追加存储,并设置header参数为False,防止列名重复存储

11 lsjz.to_csv(file_path, mode=\'a\', index=False, header=False)

12 print(\'存储成功\')

13 except Exception as e:

14 print(\'存储失败\')

  这里,结果变量是 get_jjjz() 方法中传递的历史权益数据。

  遍历每一页

  我们之前定义的get_index()方法需要接受参数page,代表页码。这里,因为不同基金的数据页不一样,我们需要得到最大页数才能遍历所有页。当然,我们也可以用一些巧妙的方法来解决这个问题。页码遍历代码如下:

   1 def main():

2 \'\'\'

3 遍历每一页

4 \'\'\'

5 flag = True

6 page = 1

7 while flag:

8 try:

9 index_page(page)

10 time.sleep(random.randint(1, 5))

11 page += 1

12 except:

13 flag = False

14 print(\'似乎是最后一页了呢\')

  它的实现方法结合了try...except和while方法来逐页遍历下一页的内容。当页数超过时,即不存在,index_page()运行时会报错。这时候可以把flag改成False,那么下一次while循环就不会继续了,这样我们就可以遍历所有的页码了。

  至此,我们的基金净值数据爬虫已经基本完成,终于可以直接调用main()方法运行了。

  

  总结

  在本文中,我们使用 Selenium 来演示基金股权页面的抓取。有兴趣的同学可以尝试使用其他条件来爬取基金数据,比如设置数据的起止日期:

  

  使用日期来抓取内容可以促进未来的数据更新。另外,如果觉得浏览器弹窗比较烦,可以试试Chrome Headless模式或者使用PhantomJS爬取。

  至此,基金净值爬虫的分析正式结束。

  

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线