本文介绍使用Python爬虫技术快速获取知网1000多篇某个主题的文章的题目,作者,作者单位,引用次数,下载次数,发表刊物,发表时间,以及文章摘要。
学习爬虫开始,我就想着对CNKI主题文献进行爬虫,对感兴趣的主题文章进行抓取,获取相关文章的基本信息和摘要,方便快速了解某一个领域的研究进程,重点等等。
经过不断的修改,终于完成此目的。
在此爬虫程序编写过程,需要注意几个问题。
1. 选择合适的网站入口;
一般我们进入知网是,www.cnki.net,我发现对此网站进行爬取,难以获取完整的网站源码,最后发现 http://search.cnki.net/ 能得到完整源码。
2. 分析网站URL:
将图中的URL复制出来,得到下面的URL:http://search.cnki.net/search.aspx?q=%E7%B2%BE%E5%87%86%E6%89%B6%E8%B4%AB&rank=citeNumber&cluster=all&val=&p=0发现被编码了,需要对复制出来的URL使用 urllib.unquote() 解码。
分析解码后的URL:http://search.cnki.net/search.aspx?q=精准扶贫&rank=citeNumber&cluster=all&val=CJFDTOTAL&p=0 ;通过选择不同的排序,不同的搜索关键词,以及不同的文献类型,可以确定 'q='字段控制搜索的关键词,'rank=' 控制排序方式,“p=” 字段控制了翻页, 并且步长为15。 最终确定搜索URL为:url='http://search.cnki.net/search.aspx?q='+str(keywords)+'&rank=citeNumber&cluster=all&val=CJFDTOTAL&p='+'number'。
3. 转码问题:
在爬取网页过程中,发现得到的网页源码打印出来后,是乱码,最终发现是因为网页源码的编码方式并不是UTF-8,因此,为了得到可以用于解析的的网页源码,需要对网页源码进行转码,首先从原始编码方式转化为通用型的Unicode,然后再转码为:UTF-8, 转码方式:
f=requests.get(test,headers=headers) # 获得网页源码
ftext=f.text.encode(f.encoding).decode('utf-8') # 转码成可以分析的编码
4. 代理IP问题:
在爬虫过程中,爬取少量网页和爬取大量网页存在较大区别。爬取较少网页是,被爬的服务器不会拒绝,当访问过多,就可能造成拒绝访问,被屏蔽IP的问题。这个时候就需要使用代理IP去访问。 代理IP实现“瞒天过海”之计,让被访问的服务器显示的IP地址是爬虫程序设定的IP,而非计算机真实的IP地址,就算发现是爬虫程序,被屏蔽的IP是代理IP,而非真实的计算机IP地址。
对CNKI网站进行爬虫时,发现当爬取450篇信息后,IP 就会被屏蔽,需要填写验证码,导致无法连续爬取,因此需要设置代理IP。 代理IP地址可以去一些网站去爬取,本程序使用了:http://www.xicidaili.com/nt/提供的免费代理IP。
5. 分功能设置函数:
为了使得代码逻辑清晰,可以分不同的目的设置不同的函数。注意各个函数之间的参数传递,容易出错。
6. 网站源码自身的不规则:
在测试程序过程中,大部分文章爬取成功,但是,存在小部分文章爬取失败,按照道理,编码一样话,那应该同时成功,同时失败。选择爬取“成功”和“失败”两篇文章的网页源码,发现存在不一致,需要通过技术处理,得到一致结果:
例如:if len(ftext_r.xpath('//ul[@class='break']/li/text()'))==3:
ws.cell(row=num+1, column=6).value=ftext_r.xpath('//ul[@class='break']/li[2]/text()')[0]
ws.cell(row=num+1, column=7).value=ftext_r.xpath('//ul[@class='break']/li[3]/text()')[0]
if len(ftext_r.xpath('//ul[@class='break']/li/text()'))==4:
ws.cell(row=num+1, column=6).value=ftext_r.xpath('//ul[@class='break']/li[3]/text()')[0]
ws.cell(row=num+1, column=7).value=ftext_r.xpath('//ul[@class='break']/li[4]/text()')[0]
这里 ul 标签 class属性为 break 下 可能存在4 li 标签, 也能是3个 li标签,所有通过判断语句,然后分别处理获取需要的信息。
7. 关键点设置提示信息:
在爬取较多网页时,难保证不出问题,需要不断改进程序的适应度,但是,若果出现问题,逐一网页排查问题的工作量太大。可以在每页爬取的时候提醒是否爬取成功。
通过这些提示信息,就可以方便的定位到某个出问题的网页,方便排查。
# ---------------------------------------
完整Python爬虫代码:
#(代码补充说明:标注“参数设置”后面的代码中可以通过简单修改,就可以爬取不同主题,不同页数,使用不同的代理IP )
*---------------------------------------
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
'''
Created on Thu Nov 9 21:37:30 2017
@author: liu kuanbin
'''
#-加载将会使用到的函数库
import requests # 读取网页
from lxml import etree # 用于解析网页
from openpyxl import Workbook # 创建表格并用于数据写入
from bs4 import BeautifulSoup # 解析网页
import random # 随机选择代理Ip
#--获得代理IP列表
def get_ip_list(urlip,headers2):
web_data = requests.get(urlip,headers=headers2)
soup = BeautifulSoup(web_data.text, 'lxml')
ips = soup.find_all('tr')
ip_list = []
for k in range(1, len(ips)):
ip_info = ips[k]
tds = ip_info.find_all('td')
ip_list.append(tds[1].text + ':' + tds[2].text)
return ip_list
#-从代理IP列表里面随机选择一个
def get_random_ip(ip_list):
proxy_list = []
for ip in ip_list:
proxy_list.append('http://' + ip)
proxy_ip = random.choice(proxy_list)
proxies = {'http': proxy_ip}
return proxies
#-定义获取文章列表对应的链接的函数
def get_data(urllist,headers,proxies):
j=0 # 初始化j的取值
for urli in urllist:
try:
j=j+1
num=15*(i-pagestart)+j # 第多少篇
test=str(urli)
# 是否使用代理去爬虫就在下面这一行代码中,是否添加: proxies=proxies
f=requests.get(test,headers=headers) # 设置Headers项; 这里添加是否使用代理去访问:
ftext=f.text.encode(f.encoding).decode('utf-8') # 对具体进行转码,获得可以正常读取的文档;
ftext_r=etree.HTML(ftext) # 对具体页进行 xpath 解析;
ws.cell(row=num+1, column=1).value='第'+str(num)+'篇文章信息'
ws.cell(row=num+1, column=2).value=str(ftext_r.xpath('//title/text()')[0]).replace(' - 中国学术期刊网络出版总库','') # 获得文章标题
ws.cell(row=num+1, column=3).value=str(ftext_r.xpath('//div[@class='author summaryRight']/p[1]/a/text()')) # 获得作者名字
#---------------------------
if len(ftext_r.xpath('//ul[@class='break']/li/text()'))==3:
ws.cell(row=num+1, column=6).value=ftext_r.xpath('//ul[@class='break']/li[2]/text()')[0]
ws.cell(row=num+1, column=7).value=ftext_r.xpath('//ul[@class='break']/li[3]/text()')[0]
if len(ftext_r.xpath('//ul[@class='break']/li/text()'))==4:
ws.cell(row=num+1, column=6).value=ftext_r.xpath('//ul[@class='break']/li[3]/text()')[0]
ws.cell(row=num+1, column=7).value=ftext_r.xpath('//ul[@class='break']/li[4]/text()')[0]
if len(str(ftext_r.xpath('//div[@class='author summaryRight']/p[2]/a/text()')))==2:
ws.cell(row=num+1, column=4).value=str(ftext_r.xpath('//div[@class='author summaryRight']/p[3]/a/text()'))# 获得作者单位
else:
ws.cell(row=num+1, column=4).value=str(ftext_r.xpath('//div[@class='author summaryRight']/p[2]/a/text()'))
# str(ftext_r.xpath('//div[@class='author summaryRight']/p[2]/a/text()'))
ws.cell(row=num+1, column=5).value=ftext_r.xpath('//div[@id='weibo']/input/@value')[0] # 第一作者及所属刊物及时间
ws.cell(row=num+1, column=8).value=str(ftext_r.xpath('//span[@id='ChDivKeyWord']/a/text()')) # 文章关键词
ws.cell(row=num+1, column=9).value=ftext_r.xpath('//span[@id='ChDivSummary']/text()')[0] # 获得文章摘要
print('爬虫'+str(15*(pageend-pagestart+1))+'篇文章信息的第'+str(num)+'篇爬取成功!!')
except:
print('爬虫第'+str(i)+'页中的第'+str(j)+'篇爬虫失败')
#---创建表格,待接收数据信息---#
wb = Workbook() # 在内存中创建一个workbook对象,而且会至少创建一个 worksheet
ws = wb.active # 获取当前活跃的worksheet,默认就是第一个worksheet
ws.cell(row=1, column=1).value ='No'
ws.cell(row=1, column=2).value ='Title'
ws.cell(row=1, column=3).value ='Author'
ws.cell(row=1, column=4).value ='Institute'
ws.cell(row=1, column=5).value ='Journal'
ws.cell(row=1, column=6).value ='Cites'
ws.cell(row=1, column=7).value ='Download'
ws.cell(row=1, column=8).value ='Keywords'
ws.cell(row=1, column=9).value ='Abstart'
#---------------参数设置
if __name__=='__main__':
pagestart=1
pageend=90
keywords='精准扶贫' ### 查询的主题
url='http://search.cnki.net/search.aspx?q='+str(keywords)+'&rank=citeNumber&cluster=all&val=CJFDTOTAL&p='
urlip = 'http://www.xicidaili.com/nt/' # 提供代理IP的网站
headers={
'Referer':'http://search.cnki.net/search.aspx?q=qw:%e7%b2%be%e5%87%86%e6%89%b6%e8%b4%ab&cluster=all&val=&p=0',
'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36',
'Cookie':'cnkiUserKey=158f5312-0f9a-cc6a-80c1-30bc5346c174; Ecp_ClientId=4171108204203358441; UM_distinctid=15fa39ba58f5d2-0bbc0ba0169156-31637c01-13c680-15fa39ba5905f1; SID_search=201087; ASP.NET_SessionId=glrrdk550e5gw0fsyobrsr45; CNZZDATA2643871=cnzz_eid%3D610954823-1510276064-null%26ntime%3D1510290496; CNZZDATA3636877=cnzz_eid%3D353975078-1510275934-null%26ntime%3D1510290549; SID_sug=111055; LID=WEEvREcwSlJHSldRa1FhcTdWZDhML1NwVjBUZzZHeXREdU5mcG40MVM4WT0=$9A4hF_YAuvQ5obgVAqNKPCYcEjKensW4IQMovwHtwkF4VYPoHbKxJw!!',
}
headers2={
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36'
}
#------------------------------------------
for i in range(pagestart,pageend+1):
try:
## 得到一个代理 IP
ip_list = get_ip_list(urlip,headers2) # 获得代理IP列表
proxies = get_random_ip(ip_list) # 获得随机的一个代理IP
# 获得每一页里面文章的 urllist
url_all=url+str(15*(i-1))
#获得每一页的文章具体文章信息页面的链接
response=requests.get(url_all,headers=headers) # 获得网页源码 ,proxies=proxies
# print(utf16_response.decode('utf-16'))
file=response.text.encode(response.encoding).decode('utf-8') # 对网页信息进行转化成为可以正常现实的 UTF-8格式
r=etree.HTML(file) # 获取网页信息,并且解析 使用xpath
urllist=r.xpath('//div[@class='wz_content']/h3/a[1]/@href') # 获得当前页所有文章的进入链接
# 获得每页urllist的文章信息,并且存到构建的表格中
get_data(urllist,headers,proxies)
except:
print('第'+str(i)+'页在爬取时候发生错误')
wb.save('知网文章信息汇总.xlsx') # 最后保存搜集到的数据
最终爬取到的数据:
通过得到的数据资料,可以用于接卸来的分析,研究“精准扶贫”此类主题文献的各种信息。例如: 那些单位发表的论文最多, 通过词云图可以看出此类文章主要的研究方向等等,可以用于各种有意思的分析。
此类玩数据,就留待下次分析。