金投网数据爬取-加强版
注:博主经过一个暑假的游手好闲,学习到了xpath及一些其他的有意思的小东西。对于之前爬取金投网的课题有了一些新的思路,特此卷土重来,让我们开始吧!
金投网链接: 金投网首页.
访问金投网,熟悉网站结构
对我们需要的信息做出一些整理,了解它们所在的位置,这次我们直接一些,点击下面的全部显示
我们会发现十年内的网址都在这里
Fn+F12(不同设备方法可能不同)再点击“查找器”(下图中红色圈圈处)找到我们需要的信息所在的标签
点击其中一个网址进入查看,发现还要点击一次才能到达有煤炭价格数据的网站
这时我们再次按Fn+F12对网页进行检查,找到网址原来藏在一个a标签的href中
点击该网址后终于来到了有煤炭价格数据的网址了!
继续查看我们需要的信息所在位置的标签,原来套在一个tr标签下面
思路整理
不要急着去看代码,写代码。在写代码之前要先想好逻辑,这才是最重要的,正所谓磨刀不误砍柴工,我认为这句话在编程中是最能体现的。大部分人写不出代码不是因为不会语法,而是因为连逻辑都没想好,不同的部分不能协调,最后把自己都绕晕了。
1.我们直接从有十年网址的网站 (链接)下手,从中获取出所有的网址,并保存在一个列表中;
2.我们再遍历列表中的每一个元素,从这些链接中取出有煤炭价格数据的网站的链接,同样保存在一个列表中,也可以保存在csv文件中;
3.遍历列表中的每一个元素,提取出网页中的煤炭价格数据,并保存在一个列表中,后续只需要对这个列表做一些处理即可。
代码详解
首先对金投网首页的网址进行爬取,获取其中我们需要去往的网址,再在我们需要去往的这些网址中获取我们需要的煤炭信息。
import sys
import requests
import numpy as np
import csv
import pandas as pd
from lxml import etree
# 使用文档解析类库
from bs4 import BeautifulSoup
# 使用网络请求类库
import urllib.request
# 输入网址
html_doc = "https://www.cngold.org/meitan/"
all_url = 'https://www.cngold.org/meitan/list_112_all.html'
if len(sys.argv)>1:
website=sys.argv[1]
if(website is not None):
html_doc= sys.argv[1]
#伪装
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36"
}
# 获取请求
req = urllib.request.Request(html_doc)
# 打开页面
webpage = urllib.request.urlopen(req)
# 读取页面内容
html = webpage.read()
# 解析成文档对象
soup = BeautifulSoup(html, 'html.parser') #文档对象
#getallhtml函数的作用是爬取all_url链接中的十年内的煤炭价格网址,并储存在列表a中
a = []
def getallhtml(all_url):
all_html = requests.get(all_url,headers = headers).content.decode('utf-8')
lx_all_html = etree.HTML(all_html)
#运用xpath,注:获得的urls是一个列表
urls = lx_all_html.xpath('//div[@class="history_news_content"]/ul/li/a/@href')
for i in urls:
#print(i),用于最初遍历列表元素,查看是否有误
one_html = requests.get(i).content.decode('utf-8')
#print(one_html)
lx_one_html = etree.HTML(one_html)
#再次使用xpath获得我们最终需要访问网站网址
last_url = lx_one_html.xpath('//div[@class="border_top"]/ul/li/a/@href')
#因为运行时发现,网站结构在十年中有所改变,具体是从2018/3/19开始,旧的网站数据结构需要用下面的xpath来获取
if last_url == []:
last_url = lx_one_html.xpath('//div[@class="left_info"]/ul/li/a/@href')[0]
a.append(last_url)
getallhtml(all_url)
allUniv = []
#函数getHTMLText用于获取url中的网页源码,并以文本形式返回
def getHTMLText(url):
try:
r = requests.get(url,timeout=30,)
r.raise_for_status()
r.encoding = 'utf-8'
return r.text
except :
pass
#函数fillUnivList用于从网页源码的文本形式中提取煤炭价格信息,并保存在allUniv列表中
def fillUnivList(soup):
date = soup.find_all('table')
for ul in date:
singleUniv = []
lspan = ul.find_all('tbody')
for span in lspan:
la = span.find_all('tr')
for a in la:
lb = a.find_all('td')
for b in lb:
singleUniv.append(b.string)
allUniv.append(singleUniv)
break
#asize函数用于将列表分割成特定长度的列表
def asize(arr,size):
s = []
c = []
for i in range(0,int(len(arr)),size):
c = arr[i:i+size]
s.append(c)
return s
#main方法将getHTMLText函数和fillUnivList函数结合在一起,便于后续直接调用
def main(url):
html = getHTMLText(url)
soup = BeautifulSoup(html.replace(' ', ' '),"lxml")
fillUnivList(soup)
#以下是最终爬取
#下面代码中文件写入时的'w'是覆盖写入
f = open(r'E://python//dizhi.csv','w',newline ='',encoding='utf-8') #文件路径、操作模式、编码 # r''
for i in a:
url = (''.join(i))
f.write(url +"\n")
f.close()
#count用于计算获取到的网址的数量
count = 0
#将获取到的URL存入指定的CSV文件
with open('E://python//dizhi.csv' ) as f:
r = csv.reader(f)
arr = list(r)
temp = np.array(arr)
t = temp.shape[0]
for i in range(0,t):
count += 1
print(count)
#我们需要的网址在a列表中,遍历a列表
for i in a:
url = (''.join(i)) #将数组中的字符串释放,即将'https://...'变为https://...
allUniv = [] #建立一个列表,用于存储爬取的数据
main(url) #调用main函数
last = []
#由于allUniv是一个双重列表,下面使用双重for循环将双重列表进行分割,将最底层的元素6个一组进行重新编排
for i in allUniv:
for j in i:
last.append(j)
last = asize(last,6)
#下面将获取的数据存入指定的文件夹中
#下面代码中的'a+'代表追加写入
with open('E://python//shuju.csv', 'a+', newline='') as csvfile:
writer = csv.writer(csvfile)
for row in last:
writer.writerow(row)
print("导入已经完成") #提示最后程序是否完成运行
注:因为这次的代码和上个版本的不一样,爬取的数据增加了好几倍!代码运行的时候需要好几分钟才能运行结束,所以小伙伴们测试代码的时候要有耐心哦,可千万别以为程序卡了就终止运行!!!
代码运行结果
光是最终需要访问的网址就有两千多条。
细心的小伙伴会发现A1单元格里的网址和最后几行的单元格里的网址格式不一样,对,毕竟差了十年呀!人家网站也会改进的嘛,由于十年间网站改进造成的影响,我在下文会总结。
最终爬下来的数据有一万多条,这还不是全部!
细心的小伙伴发现了,最后几行的数据又是那么有个性,这是因为那几个网站中的表格与我们代码中所针对的表格数据结构不同,小伙伴可以自己对照2018/10/26(链接)和2020/9/2(链接)的表格对比查看。
画图步骤并未改动,可以参看我之前未精进版的博客中的代码
问题总结
一.由于十年间网页改进,网站结构变化带来的问题:
1.首先是拥有煤炭价格数据的网站网址的爬取,但因为只有一次变化(在2018/3/20处),所以我们可以通过一个简单的if判断语句来确保一定抓到我们需要的网址并存储起来。
2.其次是煤炭数据的抓取,这里指的是表格中数据的抓取,比如下图中日期的缺失。
查看网页源代码发现在td标签下的日期存储方式并不一样,导致我们无法爬取到这种格式日期数据!
3.这次是网站的表格样式改变了,详情请看下面的两张网站表格截图。
我们针对的表格形式:
后来的表格形式:
这样我们在对爬取下来的数据进行处理和储存时就会出现截图中所出现的数据混乱现象!
二.在最后煤炭价格数据写入csv文件时,同样遇到了问题,具体错误报告如下:
从上面的csv文件截图可以看出,在2018/10/26的时候程序就中断了,博主在网上搜索了解决的方法,但还是没能解决。
最后小结
1.很高兴小伙伴们能够看到我的这篇文章,很多小伙伴可能会对代码有疑问,我也很想和小伙伴们细说,但我怕我一说就收不住又啰里啰嗦。
2.代码本身并不难,没有使用什么高级的库,都是基础,大家只需要看懂每个函数的意思和功能即可。我在代码中插入了许多注释,也是希望可以帮助大家的阅读,谢谢!