当前位置:   article > 正文

python requests爬虫——爬取李开复博客信息(包括javascript部分信息)_李开复的博客 爬虫

李开复的博客 爬虫

        今天是国庆假期第二天,已经玩了一天了,今天整理一下前两天写的数据分析作业思路,给实验报告打一下底稿。供对爬虫有兴趣的小伙伴们参考,也希望给实验没完成的同学提供一点小小的帮助。

        

  1. 任务要求

1)分析页面结构,确定待抓取的数据项,至少应抓取文章标题、发表时间、正文内容、文章URL等,可以根据选择的抓取目标的内容特点增加额外的数据项。(如抓取新浪博客时可以额外抓取标签、分类、阅读数、评论数等数据项),新闻类可以额外抓取作者新闻来源等数据项。

注意:标签、分类都是数组,应按照数组/列表形式来存储数据。

评分标准:满分35分;每个数据项5分,能够获取主要数据项4项,30分,抓取额外数据项目2项以上给满分;

2)正确处理目录页面和正文页面,能够自动抓取至少100网页内容。

评分标准:满分25分;每20条数据5分,根据爬取的数量给分超过100篇,满分;3)数据持久化。将数据存入磁盘文件。

评分标准:满分20分;根据无写入,写入文本或Excel,写入数据库确定分数;无写入0分,写入文本或Excel 10分;写入数据库20分

数据处理

评分标准:满分15分;提到数据处理5分;每个数据处理问题5分,解决两个以上数据处理问题的给予15分;

报告整体情况:

评分标准:报告格式整洁程度5分;

加分:实验报告中未要求的但在实验过程中发现新的问题,每个加5分,不超过10分。

        2.实验需要了解requests,re库,用来爬取数据文本以及提取其中的指定信息,还需要稍微了解sqlalchemy中的create_engine()方法,用于把数据存入数据库中。静态网页信息比较容易爬取,但博客网页中采用了Ajax,全称是Asynchronous Javascript and XML,即异步的JavaScript和XML.能够利用JavaScript在保证页面不被刷新,页面链接不变的情况下与服务器交换数据并更新部分网页的技术。这种情况下有两种思想获得相应的数据,一种是爬取页面被渲染前的数据,一种是爬取页面被渲染后的数据。本文先介绍爬取页面被渲染前的json文件。

        3.下面是实现代码:

  1. '''
  2. coding:requests.apparent_encoding
  3. @author: Li Sentan
  4. @time:2021.9.30
  5. @file:infoblog_requests1.py
  6. '''
  7. import requests
  8. import re
  9. from bs4 import BeautifulSoup
  10. import pandas as pd
  11. import time
  12. from sqlalchemy import create_engine
  13. #得到抓取到的网页信息的内容,返回网页源代码的text格式
  14. def getHTMLText(url):
  15. try:
  16. kv = {'user-agent':'Mozilla/5,0'}
  17. r = requests.get(url,headers = kv,timeout=30)
  18. r.raise_for_status()
  19. r.encoding = r.apparent_encoding
  20. return r.text
  21. except:
  22. return "页面请求失败!"
  23. #得到每篇博客的链接,并存到数组中
  24. def gethref(depth):
  25. scale = 80
  26. start = time.perf_counter()
  27. hrefall = []
  28. start_url = 'http://blog.sina.com.cn/s/articlelist_1197161814_0_'
  29. print("正在抓取href,请稍候".center(scale // 2, '-'))
  30. for i in range(1, depth + 1):
  31. try:
  32. url = start_url + str(i) + ".html"
  33. html = getHTMLText(url)
  34. soup = BeautifulSoup(html, "html5lib")
  35. for link in soup.find_all("a", title="", target="_blank"):
  36. href = link.get("href")
  37. href2 = href.split("/")[-1].split("_")[0]
  38. if href2 == "blog":
  39. # print(href)
  40. hrefall.append(href)
  41. # print(count)
  42. # print(link.get_text())
  43. # textall.append(link.get_text())
  44. except:
  45. continue
  46. print("抓取href用时:{}".format(time.perf_counter()-start))
  47. return hrefall
  48. #得到每篇博客的文章名称和内容,以数组形式存储。
  49. def gettext(hrefall):
  50. scale = 80
  51. start = time.perf_counter()
  52. # chinesetextall = []
  53. # englishtextall = []
  54. aidsall = []
  55. textnameall = []
  56. textall= []
  57. count = 1
  58. total = len(hrefall)
  59. print("正在抓取text和textname,请稍候".center(scale // 2, '-'))
  60. for i in hrefall:
  61. aa = '*' * (count // 5)
  62. bb = '-' * ((total - count) // 5)
  63. c = (count / total) * 100
  64. aid = i.split("/")[-1].split("_")[1]
  65. b = ""
  66. for j in range(10,16):
  67. b+=aid[j]
  68. aids = b
  69. aidsall.append(aids)
  70. soup = BeautifulSoup(getHTMLText(i),"html5lib")
  71. for link in soup.find_all("title"):
  72. textname = link.get_text().split("_")[0]
  73. if textname != "":
  74. textnameall.append(textname)
  75. else:
  76. textnameall.append("NaN")
  77. print('\n'+'抓取Textname信息有遗漏,请在网络良好的情况下运行。'.center(scale // 2, '-'))
  78. for link in soup.find_all("div", id="sina_keyword_ad_area2"):
  79. text = link.get_text()
  80. text = re.sub("[\n\t\u200b\xa0\u3000]", " ", text)
  81. # chinese = text.split("英文全文:")[0]
  82. # english = text.split("英文全文:")[1]
  83. # print(chinese, "\n", " ", english)
  84. if text != []:
  85. textall.append(text)
  86. else:
  87. textall.append("NaN")
  88. print('抓取Text信息有遗漏,请在网络良好的情况下运行。'.center(scale // 2, '-'))
  89. dur = time.perf_counter() - start
  90. print("\r{:^3.0f}%[{}->{}]{:.2f}s".format(c, aa, bb, dur), end='')
  91. count = count + 1
  92. print("\n"+"抓取text和textname用时:{}".format(time.perf_counter() - start))
  93. return textnameall,textall,aidsall
  94. #得到每篇博客的收藏数,喜欢数,阅读数,转载数,评论数,同样以数组形式存储。
  95. def getnumber(aidsall):
  96. scale = 80
  97. fall = []
  98. dall = []
  99. rall = []
  100. zall = []
  101. call = []
  102. print("正在抓取number,请稍候".center(scale // 2, '-'))
  103. start = time.perf_counter()
  104. for i in range(len(aidsall)):
  105. aa = '*' * (i//5)
  106. b = '-' * ((len(aidsall) - i)//5)
  107. c = (i / len(aidsall)) * 100
  108. urlbase = "http://comet.blog.sina.com.cn/api?maintype=num&uid=475b3d56&aids="
  109. a = getHTMLText(urlbase+aidsall[i])
  110. b1 = re.findall(r'"f":(\d+)', a)#收藏
  111. b2 = re.findall(r'"d":(\d+)', a)#喜欢
  112. b3 = re.findall(r'"r":(\d+)', a)#阅读
  113. b4 = re.findall(r'"z":(\d+)', a)#转载
  114. b5 = re.findall(r'"c":(\d+)', a)#评论
  115. if b1!= []:
  116. fall.append(int(''.join(b1)))
  117. else:
  118. fall.append('NaN')
  119. print('抓取number信息有遗漏,请在网络良好的情况下运行。'.center(scale // 2, '-'))
  120. if b2!=[]:
  121. dall.append(int(''.join(b2)))
  122. else:
  123. dall.append('NaN')
  124. # print('抓取信息有遗漏,请在网络良好的情况下运行。'.center(scale // 2, '-'))
  125. if b3!=[]:
  126. rall.append(int(''.join(b3)))
  127. else:
  128. rall.append('NaN')
  129. # print('抓取信息有遗漏,请在网络良好的情况下运行。'.center(scale // 2, '-'))
  130. if b4!=[]:
  131. zall.append(int(''.join(b4)))
  132. else:
  133. zall.append('NaN')
  134. # print('抓取信息有遗漏,请在网络良好的情况下运行。'.center(scale // 2, '-'))
  135. if b5!=[]:
  136. call.append(int(''.join(b5)))
  137. else:
  138. call.append('NaN')
  139. # print('抓取信息有遗漏,请在网络良好的情况下运行。'.center(scale // 2, '-'))
  140. # fall.append(int(float(b1[0])))
  141. # dall.append(int(float(b2[0])))
  142. # rall.append(int(float(b3[0])))
  143. # zall.append(int(float(b4[0])))
  144. # call.append(int(float(b5[0])))
  145. dur = time.perf_counter() - start
  146. print("\r{:^3.0f}%[{}->{}]{:.2f}s".format(c, aa, b, dur), end='')
  147. # print(fall,"\n",dall,"\n",rall,"\n",zall,"\n",call)
  148. return fall,dall,rall,zall,call
  149. #调用函数实现相应功能。将爬取数据存入csv文件和数据库中。
  150. def main():
  151. scale = 80
  152. start = time.perf_counter()
  153. depth =6
  154. hrefall = gethref(depth)
  155. textnameall, textall, aidsall = gettext(hrefall)
  156. fall, dall, rall, zall, call = getnumber(aidsall)
  157. try:
  158. print('\n'+"正在生成DataFrame格式,请稍候".center(scale // 2, '-'))
  159. pf = pd.DataFrame({"Textname":textnameall,"Text":textall,"href":hrefall,"收藏数":fall,"喜欢数":dall,"阅读数":rall,"转载数":zall,"评论数":call})
  160. pf.index = pf.index + 1
  161. print("正在存储csv文件,请稍候".center(scale // 2, '-'))
  162. pf.to_csv("blog_info2.csv",encoding="utf-8")
  163. print("总用时:{}".format(time.perf_counter()-start))
  164. print("正在将数据存入数据库,请稍候".center(scale // 2, '-'))
  165. connect = create_engine("mysql+pymysql://root:lst0916@localhost:3306/infoblog")
  166. pd.io.sql.to_sql(pf,'infoblog',connect,schema = "infoblog",if_exists = 'replace',index = False)
  167. print('数据爬取已完成!'.center(scale // 2, '-'))
  168. except:
  169. print('抓取信息有遗漏,请在网络良好的情况下运行。'.center(scale // 2, '-'))
  170. if __name__ == '__main__':
  171. main()

        代码有点长,其实分开来看还是比较简单,文章整体部分定义了四个函数方法,然后加一个main(),在写代码前,咱们还是明确爬取信息大致需要几个功能,然后分别定义函数,实现功能。这样思路会比较清晰。另外,由于笔者的编程能力不够强,所以我会在创建一个lianxi.py,用来练练比较陌生的函数方法,写完之后再腾到总文件中。

结果截图:

这个是校园网比较好的时候爬取的,时间用了60几秒。

定义的函数功能能够比较容易的看懂,主要要说明以下几点:

1.计时器(这个方法是在嵩天老师的课上学的):笔者在gettext(),getnumber()方法中分别加入一个计时器,相当于一个进度条,这样爬取信息的时候能够直观的看到爬取的进度,当然也能够看出来网络(用校园网的同学注意了,咳咳咳)的快慢,除此之外,网络问题也会导致你中间爬取的信息会有丢失,这点我在生成dataframe格式的时候很痛苦,因为少数据的话会生成失败。

2.看代码会发现我的try,except用的比较多,就是为了解决中间爬取的时候数据丢失的问题,丢失的数据用“NaN"表示,这样无论丢不丢失,都能生成datafame数据格式,使得代码的健壮性比较好,虽然这样增加了额外的判断,导致爬取时间延长,但还是很有必要的,毕竟我用校园网在不加try,except的时候没几次爬取成功的。

3.将数据存入数据库,说实话,笔者之前模糊的了解这一方法:create_engine(),但对数据库了解的比较少,因为上学期的数据库实在是没学好,期末的时候才看看书,sql sever也没用过几次,虽然数据库的命令比较简单,但还是忘记了,哈哈哈哈,笔者这个小垃圾,后来又搜了搜资料,看了看数据库的书,当是又一次预习了,哈哈哈哈,然后把sql server 卸载了,下了一个mysql,终于把create_engine()方法弄清楚了,把数导入数据库后,查看数据时,select * from infoblog 就ok。下面是create_engine():

  1. from sqlalchemy import create_engine
  2. import pymysql
  3. connect = create_engine("mysql+pymysql://root:lst0916@localhost:3306/infoblog")
  4. pd.io.sql.to_sql(pf,'infoblog',connect,schema = "infoblog",if_exists = 'replace')
  5. #con = pymysql.connect(host=localhost, user=username, password=password, database=dbname, #charset='utf8',use_unicode=True)
  6. # dbinfo = {
  7. # 'uesr':'root',
  8. # 'host':'@localhost:3306',
  9. # 'password':'lst0916',
  10. # 'database':'infoblog',
  11. # 'charset':'utf8',
  12. # 'use_unicode':True
  13. # }

其中,pf是数据的dataframe结构,create_engine()中的参数和pymysql.connect()中的参数是一致的,笔者的数据库dbinfo信息如上表,各位可以查看自己的数据库中的信息,按参数顺序写上。

4.主要问题以及优化点:

(1)爬取每篇博客的文章名和文章内容是比较慢的,抛开网络的问题,从代码角度来说笔者先是创建了BeautifulSoup对象,然后在其中进行了两次查找(一次是查找文章名,一次是爬取文章内容),最后把他们append对应的数组中。所以优化的时候可以合成一次查找。

(2)爬取的时候如果不加try expect 的话,在爬取javascript渲染的页面的时候特别容易漏掉信息,所以可能会报''.join()方法的错误,这是因为如果爬取信息遗漏的话join()中的参数为空,所以报错,而傻傻的笔者在之前运行的时候有时候报错,有时候不报错,还以为是''.join()方不稳定,所以,又用了int()方法,没想到还是时不时的出错,直接tm怀疑人生,tmd这么多方法不稳定,pycharm干啥吃的,哈哈哈哈,结果有一次dataframe报错的时候我忽然明白了,原来罪魁祸首是:校园网!好家伙,这下恍然大明白,写代码算着上课一共花了两天,光调这个bug用了一天半。最后,还是要好好谢谢校园网的,让我在爬虫方面得到了更多的经验,也增加了我代码的健壮性。

(3)寻找js文件,爬取javascript内容作为一个难点来说,这个确实得好好了解的,首先找到开发者工具------>network------>JS,找到对应的js文件,再爬取其中的数据。另一种方法不找js文件,是要爬取渲染后的页面,实际上就是用浏览器内核做一个虚拟的浏览器,具体实现是对selenium库函数方法使用,笔者之后会尝试将scrapy+selenium爬取数据的过程写出来。

        小白成长记。。。。。。

        Together we face the challenges of the future!

        Smile happily every day!

        

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/226604
推荐阅读
相关标签
  

闽ICP备14008679号