上周有个要好的研究生朋友找我帮忙,她最近开始准备毕业论文,需要收集一些数据。我立马就答应了,毕竟是关系很好的老同学了,怎么能不伸出援手。不过还是有些感慨,在同学眼里,学计算机好像是万能的:无论是修电脑、买电脑、做APP、咨询游戏机、爬数据,都得是样样精通。
当然我还是很愿意做这件事的,因为这对我来说也是一次很好的锻炼,一次爬虫实战,能锻炼很多方面的技术,比如:多线程、字符处理、设计思路、数据结构等等,可以说是一次综合能力的锻炼。
当然爬不同的数据难度不一样,简单了解了一下,发现要从一个官网上下载某类别下的2019年的文档,并分析内容,生成Excel。听完倒是觉得不难,便欣然立下了flag:“没问题,等我好消息”。说干就干,马上开工:
1. 获取数据
第一步就是上官网,看怎么获取数据,本以为是像之前一样,分析html页面中的数据,检索关键词。没想到比这更简单,直接调用某个接口返回json数据,甚至pagesize可以直接填1000,一次请求就能抓取全部想要的数据。这里不得不吐槽这个不对页面大小做限制也太离谱了。。。
//Run 执行爬虫 比如读取10页的数据,开启十个协程并发处理,并使用wg.Wait() 阻塞等待所有协程结束
func (s *Security) Run(begin int, end int) {
fmt.Printf("[Crawler] %s running ...\n", s.Name)
timeNow := time.Now().UnixNano()
for i := begin; i <= end; i++ {
s.wg.Add(1)
go s.GetRequest(i, client)
}
s.wg.Wait()
fmt.Printf("[Crawler] %s find %d items |run time %d ms\n", s.Name, len(s.Result), (time.Now().UnixNano()-timeNow)/1e6)
}
//GetRequest 爬虫核心代码
func (s *Security) GetRequest(page int, client *http.Client) {
//拼接页数 得到url
url := fmt.Sprintf("%s%d", s.URL, page)
fmt.Println(url)
req, err := http.NewRequest("GET", url, nil)
if err != nil {
fmt.Println("获取地址错误")
return
}
//设置请求头,尽量模仿真人请求
req.Header = s.Header
resp, err := client.Do(req)
if err != nil {
fmt.Println("get请求错误", err)
return
}
defer s.wg.Done()
defer resp.Body.Close()
//读取body数据
response, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Println("ioutil ReadAll failed :", err.Error())
return
}
}
2. 解析数据
拿到数据后就是解析json数据了,go语言自带的解析工具很好用,首先定义好对应的结构体类型(不需要的数据无需定义),然后调用json.Unmarshal()
//JSONCallBacks json结构 可以绑定json和form类型
type JSONCallBacks struct {
Result []ResultData `json:"data" form:"data"`
}
//ResultData 实际数据结构,并非真实数据
type ResultData struct {
//自增ID 存数据库用
ID int `gorm:"primary_key"`
Date string
URL string
Title string
}
obj := utils.JSONCallBacks{}
err = json.Unmarshal(response, &obj)
if err != nil {
log.Println("decode json data failed :", err.Error())
return
}
3. 保存数据
数据解析完成之后,就是保存数据了,由于是一条一条的记录,使用数据库保存是个很好的方法。因为已经定义了json结构,使用gorm保存到数据库非常容易
for _, v := range obj.Result {
//*gorm.DB
utils.DB.Create(&v)
}
4. 下载文件
根据url下载pdf文件,这步开始感觉出不对了,所有文档都是pdf结构的,无法像txt那种轻易解析,就决定先下载下来再解析。说到下载文件,就想到了python。。。
# coding = UTF-8
# 下载txt中的所有链接,没错,网上随便抄的一段代码。。。
import urllib.request
import re
import os
# 从文本中读取链接
def getUrl():
list = [ ]
count = 0
for line in open("recordback.txt"):
count = count + 1
list.append(line.strip())
print(count)
return(list)
# 下载全部连接
def getFile(url):
file_name = url.split('/')[-1]
u = urllib.request.urlopen(url)
f = open(file_name, 'wb')
block_sz = 8192 * 4
while True:
buffer = u.read(block_sz)
if not buffer:
break
f.write(buffer)
f.close()
print ("Sucessful to download" + " " + file_name)
# 从文本中读取链接
url_lst = getUrl()
# 新建文件夹
os.chdir(os.path.join(os.getcwd(), 'pdf'))
# 下载列表中的所有文件
for url in url_lst[:]:
print(url)
getFile(url)
目前为止一切都是那么的顺利,成功读取了全部文档信息,并保存到了数据库,也成功下载了pdf文件。接下来就是读取pdf内容,并分析其中的数据。等等,似乎感觉哪里不太对。。。