一:selenium 库

selenium 每次模拟浏览器打开页面,xpath 匹配需要抓取的内容。可以,但是特别慢,相当慢。作为一个对技术有追求的爬虫菜鸡,狂补了一些爬虫知识。甚至看了 scrapy 框架,惊呆了,真棒!

网上很多关于 selenium 库的详细介绍,这里略过此方法。

二: requests 库

编写一个爬虫小脚本,requests 库极为方便。接下来进入正题,如何抓取 MOOC 中国上课程的讨论内容!

1. 分析网页数据

打开你需要抓取数据的课程页面,点击讨论区之后页面加载讨论的主题内容。F12 ---> Network ---> 刷新页面。会看到里面有很多请求的内容,讨论区内容肯定是数据包,类型的话 json 文件或 xhr 文件等。

python 爬虫抓取 MOOC 中国课程的讨论区内容

 

 

 

2. 找到讨论区内容的包

按名称分析 xhr 文件,很快就会发现跟讨论区相关的文件:PostBean.getAllPostsPagination.dwr,鼠标点击文件看到该文件的详细情况。

python 爬虫抓取 MOOC 中国课程的讨论区内容

 

 

 点击 Preview ,看到的是一大段 JS 代码,是否是我们需要的内容呢,得进行验证才可以得知。

3. 分析内容包 URL进行请求

阅读里面的内容,发现 .title .nickname 等字段信息,但是都是 Unicode 编码的。试着把 .title="" 的内容复制出来直接粘贴在 python 解释器里面就会出现该编码的中文字。

 python 爬虫抓取 MOOC 中国课程的讨论区内容

 对比讨论区主题,发现是我们需要抓取的内容,

但是当我们复制 Request URL 到浏览器中进行访问时,是得不到需要的内容的,怎么办呢?

4. 根据响应去匹配需要的内容进行保存

继续分析请求头部的信息,最下面是 Request Payload , 存放了一些看不懂的数据内容,它的作用是浏览器发送请求时发送到服务器端的数据信息,和 Data Form 有些区别。但我们撸代码的时候一概作为附带的数据包发送给服务器就行了。其中几个关键的字段在代码里都会有注释信息理解,包括页码,每页数据的大小等。

5. 代码实现

  1 import requests
  2 import json
  3 import time
  4 import re
  5 import random
  6 
  7 def get_title_reply(uid, fi, http):
  8     url = 'https://www.icourse163.org/dwr/call/plaincall/PostBean.getPaginationReplys.dwr'
  9     headers = {
 10         'accept': '*/*',
 11         'accept-encoding': 'gzip, deflate, br',
 12         'accept-language': 'zh-CN,zh;q=0.9',
 13         'content-length': '249',
 14         'content-type': 'text/plain',
 15         'cookie': '',
 16         'origin': 'https://www.icourse163.org',
 17         'referer': 'https://www.icourse163.org/learn/WHUT-1002576003?tid=1206076258',
 18         'sec-fetch-mode': 'cors',
 19         'sec-fetch-site': 'same-origin',
 20         'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36',
 21     }
 22     data = {
 23         'httpSessionId': '611437146dd0453d8a7093bfe8f44f17',
 24         'scriptSessionId': '${scriptSessionId}190',
 25         'c0-scriptName': 'PostBean',
 26         'c0-methodName': 'getPaginationReplys',
 27         'c0-id': 0,
 28         'callCount': 1,
 29         # 根据主题楼主的 id 检索回复内容
 30         'c0-param0': 'number:' + str(uid),
 31         'c0-param1': 'string:2',
 32         'c0-param2': 'number:1',
 33         'batchId': round(time.time() * 1000),
 34     }
 35     res = requests.post(url, data=data, headers=headers, proxies=http)
 36     # js 代码末尾给出回复总数,当前页码等信息。
 37     totle_count = int(re.findall("totalCount:(.*?)}", res.text)[0])
 38     try:
 39         if totle_count:
 40             begin_reply = int(re.findall("list:(.*?),", res.text)[0][1:]) + 1
 41             for i in range(begin_reply, begin_reply + totle_count):
 42                 content_re ='s{}.content="(.*?)";'.format(i)
 43                 content = re.findall(content_re, res.text)[0]
 44                 # print(content.encode().decode('unicode-escape'))
 45                 fi.write('\t' + content.encode().decode('unicode-escape') + '\n')
 46                 # time.sleep(1)
 47     except Exception:
 48         print('回复内容写入错误!')
 49 
 50 
 51 
 52 def get_response(course_name, url, page_index):
 53 
 54     headers = {
 55         'accept': '*/*',
 56         'accept-encoding': 'gzip, deflate, br',
 57         'accept-language': 'zh-CN,zh;q=0.9',
 58         'content-length': '333',
 59         'content-type': 'text/plain',
 60         'cookie': '',
 61         'origin': 'https://www.icourse163.org',
 62         'referer': 'https://www.icourse163.org/learn/WHUT-1002576003?tid=1206076258',
 63         'sec-fetch-mode': 'cors',
 64         'sec-fetch-site': 'same-origin',
 65         'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36',
 66 
 67     }
 68     data = {
 69         'httpSessionId': '611437146dd0453d8a7093bfe8f44f17',
 70         'scriptSessionId': '${scriptSessionId}190',
 71         'c0-scriptName': 'PostBean',
 72         'c0-methodName': 'getAllPostsPagination',
 73         'c0-id': 0,
 74         'callCount': 1,
 75         # 课程 id
 76         'c0-param0': 'number:1206076258',
 77         'c0-param1': 'string:',
 78         'c0-param2': 'number:1',
 79         # 当前页码
 80         'c0-param3': 'string:' + str(page_index),
 81         # 页码内容量
 82         'c0-param4': 'number:20',
 83         'c0-param5': 'boolean:false',
 84         'c0-param6': 'null:null',
 85         # 毫秒级时间戳
 86         'batchId': round(time.time() * 1000),
 87     }
 88     # 代理 IP
 89     proxy = [
 90         {
 91             'http': 'http://119.179.132.94:8060',
 92             'https': 'https://221.178.232.130:8080',
 93         },
 94         {
 95             'http': 'http://111.29.3.220:8080',
 96             'https': 'https://47.110.130.152:8080',
 97         },
 98         {
 99             'http': 'http://111.29.3.185:8080',
100             'https': 'https://47.110.130.152:8080',
101         },
102         {
103             'http': 'http://111.29.3.193:8080',
104             'https': 'https://47.110.130.152:8080',
105         },
106         {
107             'http': 'http://39.137.69.10:8080',
108             'https': 'https://47.110.130.152:8080',
109         },
110     ]
111     http = random.choice(proxy)
112     is_end = False
113     try:
114         res = requests.post(url, data=data, headers=headers, proxies=http)
115         # 评论从 S** 开始,js 代码末尾信息分析
116         response_result = re.findall("results:(.*?)}", res.text)[0]
117     except Exception:
118         print('开头就错,干啥!')
119     if response_result == 'null':
120         is_end = True
121     else:
122         try:
123             begin_title = int(response_result[1:]) + 1
124             with open(course_name+'.txt', 'a', encoding='utf-8') as fi:
125                 for i in range(begin_title, begin_title + 21):
126                     user_id_re = 's{}.id=([0-9]*?);'.format(str(i))
127                     title_re = 's{}.title="(.*?)";'.format(str(i))
128                     title_introduction_re = 's{}.shortIntroduction="(.*?)"'.format(str(i))
129                     title = re.findall(title_re, res.text)
130                     if len(title):
131                         user_id = re.findall(user_id_re, res.text)
132                         title_introduction = re.findall(title_introduction_re, res.text)
133                         # print(f'user_id={user_id[0]},title={(title[0]).encode().decode("unicode-escape")}')
134                         fi.write((title[0]).encode().decode("unicode-escape") + '\n')
135                         # 主题可能未进行描述
136                         if len(title_introduction):
137                             # print(title_introduction[0].encode().decode("unicode-escape"))
138                             fi.write('\t' + (title_introduction[0]).encode().decode("unicode-escape") + '\n')
139                             get_title_reply(user_id[0], fi, random.choice(proxy))
140         except Exception:
141             print('主题写入错误!')
142     return is_end
143 
144 def get_pages_comments():
145     url = 'https://www.icourse163.org/dwr/call/plaincall/PostBean.getAllPostsPagination.dwr'
146     page_index = 1
147     course_name = "lisanjiegou"
148     while(True):
149         # time.sleep(1)
150         is_end = get_response(course_name, url, page_index)
151         if is_end:
152             break
153         else:
154             print('第{}页写入完成!'.format(page_index))
155             page_index += 1
156 
157 if __name__ == '__main__':
158     start_time = time.time()
159     print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(start_time)))
160     get_pages_comments()
161     end_time = time.time()
162     print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(end_time)))
163     print('用时{}秒!'.format(end_time - start_time))
requests 版

相关文章:

  • 2022-12-23
  • 2021-12-27
  • 2021-12-02
  • 2021-11-10
  • 2021-11-06
  • 2022-12-23
  • 2021-11-18
  • 2021-11-28
猜你喜欢
  • 2021-11-27
  • 2021-08-25
  • 2022-12-23
  • 2021-12-14
  • 2022-01-01
  • 2022-12-23
相关资源
相似解决方案