一、项目说明
数据来源:热门个股吧
数据字段:阅读、评论、标题、作者、更新时间
实现功能:读取每个股吧的全部页面的数据并写入excel表中
二、实施过程
1.明确获取网页中哪些数据
我们需要抓取的是全部发帖信息的阅读、评论、标题、作者及最后更新时间这五个字段的数据,我一开始想也不是很难,解析一下网页匹配一下对应的标签值就可以了,但后面还是出现了各种各样的问题,需要大家注意一下。
2.查看网页源代码分析结构
① 网页源代码
首先打开网页的开发者工具(Ctrl+Shift+i),在源代码中查找对应字段的标签结构。
从图中可以看出,这五个字段分别位于<span></span>
行标签内,对应的属性分别是"l1 a1"、"l2 a2"、"l3 a3"、"l4 a4"、"l5 a5"
。想必大家已经有思路了,我们可以通过先获取网页代码,再解析网页查询对应的五个字段,最后做一个提取就可以了。
② 网页链接
【某一股吧:300059】
点击查看网页链接结构:首页、第二页
可以看出个股吧链接主要由三部分组成:list、名称代码、页数
I.全部个股吧的数字代码
II. 翻页数据
如何得到不同股吧的所有翻页数据,着实让我找了好久,各种资源我都找了可惜还是没有发现,突然无意之中我找到了解决办法,我直接一个好家伙!
跟上述的五类字段一样,我们查看一下页数的代码字段,如下图所示:
我的第一个办法是直接解析网页后找到<span></span>
标签下的sumpage
属性,其内容即为总页数,本来以为原来这么好获取,结果解析完才发现,pagernums
里的内容是动态的,即span.on
是会随页而变化的,故直接requests并不能获取到,但是还是被我发现了玄机!
大家可以看data-pager
这里,里面的内容是list,300059_|452885|80|2
,我对比了几个页面后发现其中数字分别代表的是:
300059:股吧数字代码
452885:该股吧共发帖452885条
80:每个页面分别有80条贴子
2:当前所处页面为第2页
那么这时候,我们就可以直接用累积多年的算力(小学除法)算出该股吧共有452885/80=5661.0625
,向上取整共5662
页!如果你也脱口而出好家伙的话,请在屏幕下方打出来!
3.需要具备的功能
基本问题解决了,我们可以开始编写代码了。这部分不讲代码原理,只解释代码功能。如果代码存在问题或不清楚的话,欢迎大家在下方留言,我一定及时回复。
① 获取网页源代码
def getHTMLText(url):
try:
r = requests.get(url, timeout=30)
r.raise_for_status()
r.encoding = r.apparent_encoding
return r.text
except:
print("获取网页内容失败!")
② 解析网页并提取数据字段
def parsePage(html):
list = [] # 我用的二维数组存储
read = []
comment = []
title = []
author = []
time = []
try:
# print(html)
soup = BeautifulSoup(html, "html.parser")
for each in soup.find_all('span', 'l1 a1'):
if '万' in each.string:
each.string = each.string[:-2]
read.append(each.string)
read = read[1:] # read[0] == '阅读'
list.append(read)
for each in soup.find_all('span', 'l2 a2'):
comment.append(each.string)
comment = comment[1:] # comment[0] == '评论'
list.append(comment)
for each in soup.find_all('span', 'l3 a3'):
first = each.select('a:nth-of-type(1)')
for i in first:
i.find_all("a")
# print(i.title)
title.append(i.title)
list.append(title)
for each in soup.find_all('span', 'l4 a4'):
first = each.select('font:nth-of-type(1)')
for i in first:
i.find_all("font")
# print(i.title)
author.append(i.title)
list.append(author)
for each in soup.find_all('span', 'l5 a5'):
time.append(each.string)
time = time[1:] # time[0] == '最后更新'
list.append(time)
except:
print("解析网页字段失败!")
return list
③ 获取股吧总页数
基于解析的网页直接find_all也是可以的def get_total_pages_num(url):
try:
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('--headless')
chrome_options.add_argument('lang=zh_CN.UTF-8')
chrome_options.add_argument(
'User-Agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.162 Safari/537.36"')
chrome_options.add_argument('--disable-extensions')
chrome_options.add_argument('--disable-gpu')
chrome_options.add_argument('--no-sandbox')
driver = webdriver.Chrome(options=chrome_options)
driver.get(url)
page_data = driver.find_element_by_xpath(
'//div[@id="mainbody"]/div[@id="articlelistnew"]/div[@class="pager"]/span[@class="pagernums"]').get_attribute(
'data-pager')
# print(page_data)
if page_data:
# page_nums = re.findall('\|(\d+)', page_data[0])
page_nums = page_data.split("|")
# print(page_nums)
total_pages = math.ceil(int(page_nums[1]) / int(page_nums[2]))
driver.quit()
except Exception as e:
total_pages = 1
return int(total_pages)
4.抓取结果
上述代码基本的字段已经可以实现抓取了,结果如下:
可以看到,我这里的时间多了年份,这是由于研究的需要,在基于一次抓取的结果上,进行二次抓取标题所带的链接网页获得的,有关二次抓取的内容,我们再下一节再和大家分享。
总结
在抓取过程中,我还遇到了很多问题诸如:
① 部分帖子结构不同或存在冗余该如何处理(问董秘等链接)
② 抓取过程中ip被屏蔽自动跳转页面该如何处理(代理IP池)
… …
这些内容在后续章节中再和大家分享,下期再见啦!