前言:本人很菜,学习很泛。
个人博客文章原地址,阅读更加美观
由于参加数学建模的需要,在这个寒假期间小学了一下爬虫(Python学习),想着我记性这么差,还是得对这段时间的学习进行整理,以防忘记。
一、爬虫介绍
网络爬虫又称网络蜘蛛、网络机器人,是指按照某种规则在网络上爬取所需内容的脚本程序。每个网页通常包含其他网页的入口和大量信息,网络爬虫则是进入网页,定位获取所需内容。
爬虫可以划分为以下三步:
其中最重要的应该是解析数据这部分,因为这部分html来编写对应的代码,从而获取我们想要的数据。爬取部分可以通过urllib模块进行获取网页html代码。保持数据,主要有两种方法,一种是直接保存进Excel,另一种是更面向系统,保存进数据库。我学习的过程使用的是SQLite数据库引擎,小型,方便。特别是在Pycharm中dataset工具包,使得数据库运用起来是十分的方便。
二、爬取网页
平常登入浏览器上网,最频繁使用的两种请求是get请求和post请求。get请求输入的数据是直接显示在url上,安全性不够。post请求数据是不显示在url上,安全性比较高。(就比如输入密码,是直接在网页上输入,而不是写入url中)
下面代码是get和post请求和返回响应
httpbin.org是一个专门用于爬虫测试的网站
get请求
import urllib.request response = urllib.request.urlopen('http://httpbin.org/get') print(response.read().decode('utf-8'))
#响应内容 { "args": {}, "headers": { "Accept-Encoding": "identity", "Host": "httpbin.org", "User-Agent": "Python-urllib/3.9", "X-Amzn-Trace-Id": "Root=1-61f3ef09-6616eeab295d5103700d9757" }, "origin": "112.50.41.143", "url": "http://httpbin.org/get" }
post请求
data = bytes(urllib.parse.urlencode({"hellow":"world"}), encoding='utf-8') response = urllib.request.urlopen('http://httpbin.org/post', data=data) print(response.read().decode('utf-8'))
#响应内容 { "args": {}, "data": "", "files": {}, "form": { "hellow": "world" }, "headers": { "Accept-Encoding": "identity", "Content-Length": "12", "Content-Type": "application/x-www-form-urlencoded", "Host": "httpbin.org", "User-Agent": "Python-urllib/3.9", "X-Amzn-Trace-Id": "Root=1-61f3f1af-3c727e890ee174572c7b86fd" }, "json": null, "origin": "112.50.41.143", "url": "http://httpbin.org/post" }
从上面的响应headers中,可以发现User-Agent的内容是Python-urllib/3.9,说明服务器端知道我们访问的环境,也就是说服务器知道我们是爬虫,对于一些设有安全性的网页会拒绝我们访问。
假如我们通过上述方式正常访问豆瓣网页,会发现出现了如下418警告。
response = urllib.request.urlopen(r'https://movie.douban.com') print(response.read().decode('utf-8'))
#警告 urllib.error.HTTPError: HTTP Error 418:
那么是不是说明设有安全性的网页就没法访问了,显然,肯定是有办法解决的。
伪装浏览器访问头,成功爬取。
url = r'https://movie.douban.com' headers = { 'User-Agent': r'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.9 Safari/537.36', 'Cookie': 'bid=7mM480uOFCo; dbcl2="252912187:UtqLjbzdBdY"; ck=6DJI; _pk_ref.100001.4cf6=["","",1642433569,"https://accounts.douban.com/"]; _pk_ses.100001.4cf6=*; __utma=30149280.116081932.1642433569.1642433569.1642433569.1; __utmc=30149280; __utmz=30149280.1642433569.1.1.utmcsr=accounts.douban.com|utmccn=(referral)|utmcmd=referral|utmcct=/; __utmz=223695111.1642433569.1.1.utmcsr=accounts.douban.com|utmccn=(referral)|utmcmd=referral|utmcct=/; __utmc=223695111; __utmb=223695111.0.10.1642433569; __utma=223695111.1563560722.1642433569.1642433569.1642433569.1; __gads=ID=c847fa157ddd2521-2202472700d0001e:T=1642433572:RT=1642433572:S=ALNI_Ma_fS0aJS2enPjG0sgam_fOP8ZAfA; push_doumail_num=0; push_noty_num=0; __utmt=1; __utmv=30149280.25291; __utmb=30149280.2.10.1642433569; _pk_id.100001.4cf6=b998428814f5d240.1642433569.1.1642434417.1642433569.; Hm_lvt_eaa57ca47dacb4ad4f5a257001a3457c=1642433569,1642433596,1642434417; Hm_lpvt_eaa57ca47dacb4ad4f5a257001a3457c=1642434417' }
req = urllib.request.Request(url=url, headers=headers) response = urllib.request.urlopen(req) print(response.read().decode('utf-8'))
三、解析数据
主要使用python的bs4库和正则表达式re库,bs4中的BeautifulSoup类定义html,方便定位到对应标签。
BeautifulSoup中最常用的.find_all(),可用于字符串、正则表达式、函数匹配。
BeatifulSoup
from bs4 import BeautifulSoup
file = open('./baidu.html', 'rb') html = file.read()
bs = BeautifulSoup(html, 'html.parser') # print(bs)
# print(bs.title) # print(bs.a) # print(bs.head) # print(type(bs.head)) # print(bs.title.string) # print(bs.a.attrs)
#1.Tab 标签及其内容,第一个 #2.NavigableString 标签里的内容 #3.BeautifulSoup 整个文档 #4.Comment 是一个特殊的NavigableString 输出内容不包括注释 # print(type(bs)) # print(bs.name) # print(bs.attrs) # print(bs)
#—————————————–
#文档的遍历 # print(bs.head.contents[1])
#文档的搜索 #(1)find_all #字符串过滤: 查找与字符串完全匹配的内容 # t_list = bs.find_all('a') # print(t_list)
#正则表达式搜素: 使用search()方法匹配内容 # t_list = bs.find_all(re.compile('a')) # print(t_list)
#方法: 传入一个函数,根据函数要求搜索 (了解) # def name_is_exists(tag): # return tag.has_attr('name') # # t_list = bs.find_all(name_is_exists) # print(t_list)
#(2)kwargs 参数
# t_list = bs.find_all(id = "head") # t_list = bs.find_all(class_=True)
# (3)text参数 # t_list = bs.find_all(text='hao123') # t_list = bs.find_all(text=['hao123', '地图', '贴吧']) # t_list = bs.find_all(text=re.compile('d')) #正则
#(4)limit参数 # t_list = bs.find_all('a', limit=3)
#css选择器 # t_list = bs.select('title') #标签 # t_list = bs.select('.mnav') #类名 # t_list = bs.select('#u1') #id # t_list = bs.select("a[class='bri']") #属性 # t_list = bs.select("head > title") #子标签 # t_list = bs.select(".mnav ~ .bri") #
# print(t_list[0].get_text())
baidu.html
<!DOCTYPE html> <html> <head> <meta content="IE=Edge" http-equiv="X-UA-Compatible"/> <meta content="always" name="referrer"/> <link href="https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css"rel="stylesheet" type ="text/css" /> <!– 具体显示的样式–> <title>百度一下,你就知道啦</title> </head> <body link="#0000cc"> <div id="wrapper"> <div id="head"> <div class="head_wrapper"> <div id="u1"> <a class="mnav" href="http://news.baidu.com" name="tj_trnews">!–新闻-</a> <a class="mnav" href="http://news.baidu.com" name="tj_trnews">新闻</a> <a class="mnav" href="https://www.hao123.com" name="tj_trhao123">hao123</a> <a class="mnav" href="http:/ /map.baidu.com" name="tj_trmap">地图</a> <a class="mnav" href="http://v.baidu.com" name="tj_trvideo">视频</a> <a class="mnav" href="http://tieba.baidu.com" name="tj_trtieba">贴吧</a> <a class="bri" href="//www.baidu.com/more/" name="tj_briicon" style="….">更多产品</a> </div> </div> </div> </div> </body> </html>
正则表达式用于定位到所需内容,非常实用。
. | 匹配除换行符 n 之外的任何单字符 | |
( ) | 分组标记,内部只能使用 | 操作符 |
* | 前一个字符0次或无限次扩展 | abc*表示ab、abc、abcc、abccc等 |
+ | 前一个字符一次或无限次扩展 | abc+表示abc、abcc、abccc等 |
? | 前一个字符0次或者1次扩展 | abc?表示ab、abc |
[ ] | 字符集,对单个字符给出取值范围 | [abc]表示a、b、c,[a-z]表示a到z单个字符 |
表达式左右任意一个 | ||
{m} | 扩展前一个字符m次 | ab{2}c表示abbc |
{m,n} | 扩展前一个字符m至n次 | ab{1,2}c表示abc,abbc |
^ | 匹配字符串开头 | ^abc表示abc且在一个字符串的开头 |
$ | 匹配字符串结尾 | abc$表示abc且在一个字符串的结尾 |
d | 数字,等价于[0-9] | |
w | 单词字符,等价于[A-Za-z0-9_] |
import re
pat = re.compile('AA') #此时的AA是正则表达式 m = pat.search('AACDBAA') #search字符串被校验的内容 print(m)
m = re.search('asd','Aasd') m = re.findall('a', 'ASDdadaSJJAa') m = re.findall('[A-Z]+', 'ASDdaAdaSJJAa') m = re.sub('a', 'A', 'adadfafs') #第一个被替换,第二个替换后,第三个对象 a = r'dafafafa/faffafaffasddfx' # print(m)
将正则表达式应用于html解析
如前面的baidu.html,假如我们需要类为mnav的href内容,只需要如下代码,十分便利
import re from bs4 import BeautifulSoup
file = open('./baidu.html', 'rb') html = file.read() bs = BeautifulSoup(html, 'html.parser') findhref = re.compile(r'<a class="mnav" href="(.*?)"') result = re.findall(findhref, str(bs)) print(result)
四、保存数据
主要用到xlwt(excel)和sqlite3(database)库,下面只介绍sqlite3。
sqlite是一个轻型的数据库,创建十分方便,只需要几行代码。适用于数据量不多的项目,对于爬虫是足够了。
#数据库的创建和初始化 def init_db(dbpath): sql = ''' create table headImages( 标题 varchar(20), 日期 datatime, 图片链接 text )
''' conn = sqlite3.connect(dbpath) cursor = conn.cursor() cursor.execute(sql) conn.commit() conn.close()
其实对于数据库的各个sql语句的执行,都是直接通过游标cursor()执行,一行代码解决,没有什么困难。难的就是需要把所有的各个方方面面整合在一起,对于大多数项目其实都是这样,对于学习单个内容其实都蛮简单。
#insert into将数据插入table即可 def saveData2DB(dataList, dbpath): init_db(dbpath) conn = sqlite3.connect(dbpath) cur = conn.cursor()
for data in dataList: for index in range(len(data)): data[index] = '"'+data[index]+'"' sql = ''' insert into movie250(info_link,pic_link,cname,ename,score,rated,instroduction,info) values(%s) '''%','.join(data) # print(sql) cur.execute(sql)
conn.commit() cur.close() conn.close()
实战
写了爬取头像的代码,并且根据自己喜好去爬。还没有做web可视化,做好就可以直接在网站上实时更新并选择自己喜欢的头像,有时间再做。
代码可执行
# — coding: utf-8 — # @Author: zrs # @Time: 2022-02-07 14:53 # @File: main.py
import re import xlwt import sqlite3 import numpy as np import urllib.request from bs4 import BeautifulSoup
url = r'https://www.woyaogexing.com/touxiang/qinglv/'
def askURL(url): headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:96.0) Gecko/20100101 Firefox/96.0', 'Cookie': 'BAIDU_SSP_lcr=https://www.baidu.com/link?url=pCdSJIVWq3oRa2SQ63p2b2G6MhUGFLwmqAr4tM9pP-HMoNZVYwtunks5FpRiI6X7&wd=&eqid=bc47740e0007c8de000000056200c13b; Hm_lvt_a077b6b44aeefe3829d03416d9cb4ec3=1643031884,1644216664,1644216690; __gads=ID=03cc11a055dcdbab-2294c11433d00095:T=1643031885:RT=1643031885:S=ALNI_MYbVQjpAjO2k9fH36n0Lkm_6gkiYA; Hm_lpvt_a077b6b44aeefe3829d03416d9cb4ec3=1644216740' }
request = urllib.request.Request(url, headers=headers) html = ''
try: response = urllib.request.urlopen(request) html = response.read().decode('utf-8') except urllib.error.URLError as e: if hasattr(e, 'code'): print(e.code) if hasattr(e, 'reason'): print(e.reason)
return html
def getData(base_html):
soup = BeautifulSoup(base_html, 'html.parser') # print(soup) findHref = re.compile(r'<a class="img" href="(.*?)"') findTime = re.compile(r'<div class="artTime y">(.*?)</div>')
features = ['可爱', '动漫', '手绘', '搞怪'] dataList = [] for txList in soup.find_all('div', class_='txList'): title = txList.find_all('a')[1].string data = [] for feature in features: if feature in title: data.append(title) #解析时间 html = str(txList) childURL = 'https://www.woyaogexing.com'+''.join(re.findall(findHref, html)) child_html = askURL(childURL) childSoup = BeautifulSoup(child_html, 'html.parser') time = re.findall(findTime, str(childSoup)) data.append(time) #解析图片链接 lazy = childSoup.find_all('img', class_='lazy') imgURL = re.findall(r'src="(.*?)"', str(lazy)) data.append(imgURL) # data = data + imgURL dataList.append(data) break return dataList
#excel保存 # def saveData(dataList, savePath): # wordbook = xlwt.Workbook(encoding='utf-8') # wordsheet = wordbook.add_sheet('豆瓣电影Top250', cell_overwrite_ok=True) # columns = ['title', 'time', 'imgURL'] # for i in range(3): # wordsheet.write(0, i, columns[i]) # for i in range(len(dataList)): # data = dataList[i] # for j in range(len(dataList[0])): # wordsheet.write(i+1, j, 'n'.join(data[j])) # wordbook.save(savePath)
def init_db(dbpath): sql = ''' create table headImages( 标题 varchar(20), 日期 datatime, 图片链接 text )
''' conn = sqlite3.connect(dbpath) cursor = conn.cursor() cursor.execute(sql) conn.commit() conn.close()
#数据库保存 def saveData(dataList, dbpath): init_db(savaDB) conn = sqlite3.connect(dbpath) cur = conn.cursor()
for data in dataList: data[0] = "'" + data[0] + "'" data[1] = "'" + 'n'.join(data[1]) + "'" data[2] = "'" + 'n'.join(data[2]) + "'" # data[index] = list(data[index]) sql = """ insert into headImages(标题,日期,图片链接) values(%s) """%','.join(data) # print(sql) cur.execute(sql)
conn.commit() cur.close() conn.close()
if __name__ == '__main__': savePath = r'./头像.xls' savaDB = r'./头像.db' html = askURL(url) dataList = getData(html) # print(dataList) # saveData(dataList, savePath) saveData(dataList, savaDB)
神龙|纯净稳定代理IP免费测试>>>>>>>>天启|企业级代理IP免费测试>>>>>>>>IPIPGO|全球住宅代理IP免费测试