结合我之前大半年的写爬虫经历,遇到了各种问题,然后最近这段时间也不会怎么碰爬虫,所以我结合之前的经历和遇到的各种坑,写了这一篇总结性的文章。
首先和大家说一下python数据分析,如果大家不知道python对数据分析的流程大体是什么样,我看了一个总结的比较到位的博客,把它摘录到这里了。
- 一:数据抽取
从外部源数据中获取数据
保存为各种格式的文件、数据库等
使用Scrapy爬虫等技术 - 二:数据加载
从数据库、文件中提取数据,变成DataFrame对象
pandas库的文件读取方法 - 三:数据处理
数据准备:
对DataFrame对象(多个)进行组装、合并等操作
pandas库的操作
数据转化:
类型转化、分类(面元等)、异常值检测、过滤等
pandas库的操作
数据聚合:
分组(分类)、函数处理、合并成新的对象
pandas库的操作 - 四:数据可视化
将pandas的数据结构转化为图表的形式
matplotlib库 - 五:预测模型的创建和评估
数据挖掘的各种算法:
关联规则挖掘、回归分析、聚类、分类、时序挖掘、序列模式挖掘等 - 六:部署(得出结果)
从模型和评估中获得知识
知识的表示形式:规则、决策树、知识基、网络权值
大家以后想学数据分析,可以按照上面流程来学习,而我今天主要讲的是爬取信息部分。爬取信息也是很关键的一步,这也是我这篇博客的主要内容,大家可以跟随我的文章一起看一下,这篇博客不是偏向基础的,是一个爬虫理解和我个人心得的文章,因为入门的有很多,也不缺我这篇,只希望大家可以从我的经验中学习,也可以及时发现我的错误。
1.python中文转换url编码
有很多时候,我们需要爬取搜索引擎,当你输入关键词后,形成了一个特定的网址,比如https://search.sina.com.cn/?q=%BA%FE%B1%B1%BE%AD%BC%C3%D1%A7%D4%BA&c=news&from=index,但是我输入的关键词是湖北经济学院,但其中出现一串莫名其妙的%BA%FE%B1%B1%BE%AD%BC%C3%D1%A7%D4%BA。
其实这个就是我们输入的关键词湖北经济学院,只不过是一个URL编码。
中文转换url编码
import urllib.parse data = '杭州' print (data) 杭州 data urllib.parse.quote(data)#转成url编码 '%E6%9D%AD%E5%B7%9E'
大家在看一个例子
import urllib.parse urllib.parse.quote('湖北经济学院') Out[2]: '%E6%B9%96%E5%8C%97%E7%BB%8F%E6%B5%8E%E5%AD%A6%E9%99%A2'
为什么这个和上面的我复制的那个新浪搜索不一样呢?其实这里还有一个原因就是URL编码问题,一般urllib.parse.quote()默认转成utf-8编码,而新浪这个是gbk编码。
所以我们应该这样写
import urllib.parse urllib.parse.quote('湖北经济学院'.encode('gbk')) Out[3]: '%BA%FE%B1%B1%BE%AD%BC%C3%D1%A7%D4%BA'
大家记住常用的网址编码和网页编码有gbk,gb2312,utf-8三种,出现乱码情况的时候大家记得转编码,
最后教大家一个小技巧,可以转回去。
url编码转中文
import urllib.parse urllib.parse.unquote('%E6%9D%AD%E5%B7%9E') Out[10]: '杭州'
2.请求设置
在很多时候我们请求网页会返回403错误,刚学爬虫的同学在开始写爬虫的时候要了解一下网页结构,比如说网页的编码有哪几种?网页的请求方式以及最后的返回的状态码,这都是写爬虫之前必须了解的东西。
大家要记住爬虫爬取网页信息就像我们自己在浏览网页一样,只不过我们平时用的浏览器把这些部分都省去了,一般只有检查之后才会看到我们请求网页的实质,这里我就不过多的讲解,可以看一下我找的几篇链接。
1.资源推荐
我这里只是一个大致的推荐,具体每一块想学好,但看这几篇博客还是不行的,需要大家多查阅资料学习。
2.爬取示范
import requests def get_data(url): headers={"User-Agent": 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36'}#不加请求头,电脑很可能默认为爬虫,拒绝你的请求 r = requests.get(url,headers=headers) r.raise_for_status()###################查看是否正常,正常则返回200,如有异常则返回404等。 r.encoding = 'utf-8'#网页编码一般默认utf-8,入股爬下来信息乱码,可以适当调整一下。 return r.text#返回网页文本 get_data('https://www.baidu.com/')#请求百度网页
我们一般打开浏览器请求一个网页的时候,相当于电脑给他发送了一个信息,这里面就有很多参数,比如headers,cookies,但cookies一般是登录的时候用的。一般我们在设置的时候需要加headers,不然很有可能被拒绝访问,然后返回状态码403。
至于对headers方面的知识,我也就不罗列,推荐几个我认为写的还可以的博客。
- 爬虫入门到精通-headers的详细讲解(模拟登录知乎)
- Python爬虫实践(三)设置Headers
- Python网络爬虫原理(代理/超时/重定向/Proxy/headers设置/httplib/urllib实现)
其实headers也是很复杂的,到后面我会具体举例说。
这里推荐大家一个抓包器可以看我们正常请求网页发送的信息。
https://pan.baidu.com/s/1YRUuPQv1J_M9tN9rCqiP6Q(提取码:81ww)
补充
有的时候爬虫程序运行没问题,但没显示结果或结果不全。
1)有些网站针对每一个ip,单位时间内只能访问一定次数,至于单位时间是多少看网站规定吧,超过这个次数就不能访问爬去 了,就只有等了,等多久,看那个网站的规定吧。
2)可以运行,没有结果,那要么是选择器问题,换一个选择器;要么是选择器里面的内容有问题导致没有爬取到结果。
这个时候我们就要考虑代理ip 的设置,虽然有那种免费的代理IP但是不稳定,不建议大家使用,大家可以直接购买网上的代理。下面这个是网上的一个付费代理的,感觉还行。
def get_data(url): r = requests.get(url,headers=headers,proxies=proxies,timeout=30) r.raise_for_status()###################查看是否正常,正常则返回200,如有异常则返回404等。 r.encoding = 'utf-8' return r.text # targetUrl = "http://test.abuyun.com" #targetUrl = "http://proxy.abuyun.com/switch-ip" #targetUrl = "http://proxy.abuyun.com/current-ip"
# 代理服务器 proxyHost = "http-dyn.abuyun.com" proxyPort = "9020"
# 代理隧道验证信息 proxyUser = "***********" proxyPass = "************"
proxyMeta = "http://%(user)s:%(pass)s@%(host)s:%(port)s" % { "host" : proxyHost, "port" : proxyPort, "user" : proxyUser, "pass" : proxyPass, }
proxies = { "http" : proxyMeta, "https" : proxyMeta, }
我的第一个爬虫
其实也不算是我写的第一个爬虫,是我写的一篇微信推文讲解的第一个爬虫,这里我就把代码放在上面,有兴趣的,大家可以在自己电脑上面玩一下。
import requests#调用对网页请求的包 from bs4 import BeautifulSoup#BeautifulSoup解析的包,主要用于解析和提取内容 title =[]#生成一个存储动漫名的包 Playvolume =[]#生成一个存储播放量的列表 attention = []#生成一个存储关注数的列表 Barrage =[]#生成一个存储弹幕量的列表 url ='https://www.bilibili.com/ranking/bangumi/13/0/3' data = requests.get(url).text#请求这个网页的,返回网页文本 soup = BeautifulSoup(data,'lxml')#爬取下来的网页进行解析,lxml是常用的解析器,速度快 content_list=soup.find_all('ul',attrs={'class':'rank-list bangumi'})[0] #利用find_all()提取的结果是存放在一个列表中,所以提取第一项,方面后面的提取,因为BeautifulSoup对列表无法定位 a = content_list.find_all('li',attrs={'class':'rank-item'}) #这一步是为了提取排行榜所以一步步的提取,find_all()函数中的第一个参数是标签名,attrs参数是属性值 print('排名',' 名字',' 播放量',' 弹幕量',' 喜欢')#这一步是小编为了打印结果美观,让输出的信息看起来像一个表格形式 for i in range(len(a)):#循环提取每一块中的信息,因为五十个动漫网页结构都是成块状 list = a[i].find_all('div',attrs={'class':'info'})[0] title.append(list.find('a').text) list1 = list.find_all('div',attrs={'class':'detail'})[0] #我也不想写这么多的找标签,BeautifulSoup定位是一步步下推,小编也不想这样 Playvolume.append(list.find_all('span',attrs={'class':'data-box'})[0].text) Barrage.append(list.find_all('span',attrs={'class':'data-box'})[1].text) attention.append(list.find_all('span',attrs={'class':'data-box'})[2].text) print(i+1,title[i],Playvolume[i],Barrage[i],attention[i])#格式化输出每一步动漫,小编为了你们真是绞尽脑汁
这上面的注释很详细,如果还是不懂的话,你们可以看一下我的第一个爬虫那篇博客,具体细节的话,我就不过多的描述,那篇博客暂时还没发(更新中),大家不用急,我会尽快发布。
爬虫爬取流程
基础介绍
爬虫爬取分为三步,爬取,提取,保存。这是我个人归纳的三步,但很直观,比较容易懂。但爬取我上面都大致讲过了,核心就是提取这一步。
这里我们常用三种方法:
- 正则表达式(采用模糊匹配的方式,找出我们所需要内容)
- BeautifulSoup(是一个可以从HTML或XML文件中提取数据的第三方Python库), BeautifulSoup可以采用Python自带的html.parse作为它的解析器,也可以采用lxml作为解析器。lxml 是Python语言解析处得XML和HTML的库
- XPath(XPath 是一门在 XML 文档中查找信息的语言,XPath 可用来在 XML 文档中对元素和属性进行遍历。)
至于三种方法具体怎么用,我会大致推荐一些链接。有兴趣的可以自己看链接,这些也是需要多练习的。
参考资料
- 爬虫处理流程及网页解析
- [python爬虫] 正则表达式使用技巧
- 正则表达式 – 语法
- Python爬虫利器二之Beautiful Soup的用法
- python beautiful soup库的超详细用法
- python爬虫系列之 xpath
- XPath编写规则学习
在这三种方法中,万能的办法就是正则表达式提取,其次是xpath,但是xpath虽然好用,但有的时候提取不了东西,这是唯一的缺点,三个方法各有好处也都有缺点。
下面我会对这些提取进行一些示范和列举一些不常用但很有用的方法。
实践举例
下面这个代码是截取我之前爬取搜狗微信公众号代码想要具体用的话可以去我的那篇博客看一下。
title_1=selector.xpath('//div[@class="txt-box"]/h3')[i] title2 = title_1.xpath('string(.)').replace('n','')#爬取一个网站新闻的标题,但是属于一个标签,分为了多段。这个可以把同属一个标题中的文段拼接起来。
正则表达式的一个例子,这个是匹配日期字段的一个实例,用其他方法很难解决,只能用正则。
import re a ='澎湃新闻记者 钟煜豪 2019-04-17 21:08:00' mat =re.search(r"(d{4}-d{1,2}-d{1,2})",a) b =mat.group(0) print(b) 2019-04-17
至于更多细节问题,大家还是多写多熟悉才会掌握这些用法。这里就不过多的重复了。
数据保存
在平时我们爬取信息主要是为了让它变成我们想要的数据,保存在我们的电脑或者数据库中,这也常是保存的两种情况。
保存文件
这里我举个例子,比如我爬一个beyond的数据,想把它存为一个csv文件,这里特别强调一下,大家以后可以保存csv尽量保存为csv文件,最好不要保存为excel文件格式,这样不方便后期处理。下面我就直接给出一个完整代码,具体细节不强调了,大家要记得调相应的包,然后我是用的selenium包。这个下次博客我会展开来讲。
from selenium import webdriver import time from datetime import datetime import urllib import os import pandas as pd os.chdir('C:/Users/离殇/Desktop') # 将当前工作目录改变为`C:/Users/Administrator/Desktop` #print(os.getcwd()) # 打印当前工作目录
url='http://weixin.sogou.com/weixin?type=2&s_from=input&query=beyond&ie=utf8&_sug_=n&_sug_type_=' driver=webdriver.Chrome("C:/Users/离殇/Desktop/1/chromedriver.exe") time.sleep(1) driver.get(url) time.sleep(1)
def get_url(url): headers={'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36'} request=urllib.request.Request(url=url,headers=headers) response=urllib.request.urlopen(request) web=response.read().decode('utf8') return web
####先建立一个文件夹吧 if not os.path.exists('C:/Users/离殇/Desktop/beyond/html'): os.makedirs('C:/Users/离殇/Desktop/beyond/html')##创建文件夹
csv='C:/Users/离殇/Desktop/beyond/beyond.csv' if not(os.path.exists(csv)): data1 = pd.DataFrame(columns=['类型', '标题', '发布时间','当下时间']) data1.to_csv(csv, index = None,encoding="utf-8")##index = None
###主体函数 for page in range(4): types=[] titles=[]###注意前面要加空列表 times=[] titles=driver.find_elements_by_xpath('//h3//a') types=driver.find_elements_by_xpath('//*[contains(concat( " ", @class, " " ), concat( " ", "account", " " ))]') times=driver.find_elements_by_xpath('//*[contains(concat( " ", @class, " " ), concat( " ", "s2", " " ))]') for i in range(len(titles)): title=titles[i].text time=times[i].text typess=types[i].text url=titles[i].get_attribute('href')##得到链接 TIME = datetime.now().strftime('%Y-%m-%d %H:%M:%S')##记录一下当下时间,这个随意 try: web=get_url(url) local_url='C:/Users/离殇/Desktop/beyond/html/'+title+'.html' with open(local_url,'w',encoding='utf8') as f: f.write(web)
data1 = pd.DataFrame({'类型':typess,'标题':title,'发布时间':time,'当下时间':TIME},columns=['类型', '标题', '发布时间','当下时间'],index=[0]) data1.to_csv(csv,index = None,mode = 'a' ,header= None,sep=',',encoding="utf-8")##mode = 'a'这个表示追加,保存文件 except Exception as e: print(e)##输出错误
finally: next driver.find_element_by_link_text('下一页').click() # import time # time.sleep(1) print('第'+str(page+1)+'页') print('over')
driver.close()
其实学习爬虫很重要的一步就是用好函数和会学习别人的代码,这个代码其实也不负杂,在运行之前要装好selenium和一个浏览器驱动软件,这个要结合自己的浏览器版本来安装
这一步就是驱动这个软件,驱动自己的浏览器运行。driver=webdriver.Chrome("C:/Users/离殇/Desktop/1/chromedriver.exe")
下面我将那个chorme的下载链接放在这里。大家记得下和自己电脑对应的版本。
chromedriver下载
建议大家最好多用谷歌,谷歌浏览器对于写爬虫是很有用的。
数据库存储
下面我附上我最近写的一个新浪搜索的爬取,还有点小bug,也希望大家多多指出。然后你们也可以了解一下数据库存储。然后这个涉及到了时间格式化,时间判断,关键词判断的应用,大家可以参考一下。
from lxml import etree import datetime import requests import time from bs4 import BeautifulSoup import os import pandas as pd import pymysql import urllib.parse import time import numpy as np import math import re cuttime1=(datetime.datetime.now()-datetime.timedelta(days=1)).strftime('%Y-%m-%d') cut =2 types = False def cut_time(time_1):#时间判断函数 if(types): cut_time=datetime.date.today().strftime('%Y-%m-%d') else: cut_time = (datetime.date.today() – datetime.timedelta(days=cut)).strftime('%Y-%m-%d') cut_nb=int(cut_time.split('-')[0])*366+int(cut_time.split('-')[1])*31+int(cut_time.split('-')[2]) time_nb=int(time_1.split('-')[0])*366+int(time_1.split('-')[1])*31+int(time_1.split('-')[2]) test_1=False if(time_nb>=cut_nb): test_1 = True return test_1 def get_data(url,headers): r = requests.get(url,headers,timeout=30) r.raise_for_status()###################查看是否正常,正常则返回200,如有异常则返回404等。 r.encoding = 'gbk' return r.text #一些连接数据库的信息并且连接数据库 dbconn=pymysql.connect( host="127.0.0.1", database="fufj", password="123456", user="root", port=3306, charset='utf8mb4' ) cur=dbconn.cursor() key_word=[] sch_id=[] sql_use='SELECT sch_id,key_word FROM key_word'#从数据库中读取关键词和对应的学校id sql_stu_data = pd.read_sql(sql_use,dbconn) train_data = np.array(sql_stu_data)#np.ndarray() main_list = train_data.tolist()#list for i in range(len(main_list)): sch_id.append(main_list[i][0]) key_word.append(main_list[i][1]) sql_use='SELECT sch_id,title,web_name FROM news_web'+' WHERE time>=''+cuttime1+''' sql_stu_data = pd.read_sql(sql_use,dbconn) train_data = np.array(sql_stu_data)#np.ndarray() main_list = train_data.tolist()#list old_new = []#存放从数据库的信息,用于去重 for i in range(len(main_list)): old_new.append(main_list[i][0]+main_list[i][1]+main_list[i][2]) for j in range(len(key_word)): url_all='https://search.sina.com.cn/?q=' +urllib.parse.quote(key_word[j].encode('gbk')) + '&c=news&from=index&col=&range=&source=&country=&size=&time=&a=&page=4&pf=0&ps=0&dpc=1' headers={"User-Agent": 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36'} try: data=get_data(url_all,headers) selector = etree.HTML(data) title = [] time_1 = [] url_list =[] news_web =[] url_list1 = selector.xpath('//div[@class="result"]/div[@class="box-result clearfix"]/h2/a/@href') url_list2 = selector.xpath('//div[@class="result"]/div[@class="box-result clearfix"]/div/h2/a/@href') url_list = url_list1 + url_list2 title_list1 = selector.xpath('//div[@class="result"]/div[@class="box-result clearfix"]/h2/a/text()') title_list2 = selector.xpath('//div[@class="result"]/div[@class="box-result clearfix"]/div/h2/a/text()') title_list =title_list1 + title_list2 time_list1 = selector.xpath('//div[@class="result"]/div[@class="box-result clearfix"]/h2/span/text()') time_list2 = selector.xpath('//div[@class="result"]/div[@class="box-result clearfix"]/div/h2/span/text()') time_list = time_list1 + time_list2 # url_next1 = selector.xpath('//div[@id="_function_code_page"]/a[10]/@href') # url_next = 'https://search.sina.com.cn' + url_next1[0] for i in range(len(title_list)): # 根据一页网站上的标题数来循环爬取 if ' ' in time_list[i]: news_web.append(time_list[i].split(' ')[0]) mat =re.search(r"(d{4}-d{1,2}-d{1,2})",a) time_list[i] = mat.group(0) #time_list[i] = time_list[i].split(' ')[1] cut_unm = cut_time(time_list[i]) else: pass if not ( sch_id[j]+title_list[i]+news_web[i] in old_new): if key_word[j] in title_list: cur.execute("INSERT INTO news_web(`web_id`,`web_name`,`sch_id`,`url`,`time`,`title`) VALUES (%s,%s,%s,%s,%s,%s)", ('120',news_web[i],sch_id[j],url_list[i],time_list[i],title_list[i])) print(1111) dbconn.commit() old_new.append(sch_id[j]+title_list[i]+news_web[i]) pass else: data1 = requests.get(url_list[i],headers,timeout=30) data1.encoding = 'utf-8' soup = BeautifulSoup(data1.text,'lxml') content = soup.get_text() if key_word[j] in content:#内容判断 print(22222) cur.execute("INSERT INTO news_web(`web_id`,`web_name`,`sch_id`,`url`,`time`,`title`) VALUES (%s,%s,%s,%s,%s,%s)", ('120',news_web[i],sch_id[j],url_list[i],time_list[i],title_list[i])) dbconn.commit() old_new.append(sch_id[j]+title_list[i]) pass else: pass except: pass #关闭与数据库的连接 cur.close()
资源推荐
Python3爬虫学习之将爬取的信息保存到本地的方法详解
Python3爬虫数据入数据库
我就推荐了两个,有兴趣的自己可以多看看这方面的资料。
个人总结
爬虫的基本内容也就大致这些了,后期我会介绍selenium爬取和scrapy框架爬取以及相关的知识,然后也会涉及一些数据处理,数据清洗的内容。这也是爬虫很重要的一部分,也是我写爬虫大半年的经验,写这篇博客就是主要想和大家分享一下。然后希望大家不要踩我走过的坑,可以学习我的经验快速成长吧!
神龙|纯净稳定代理IP免费测试>>>>>>>>天启|企业级代理IP免费测试>>>>>>>>IPIPGO|全球住宅代理IP免费测试