前言:本人很菜,学习很泛。
个人博客文章原地址,阅读更加美观
由于参加数学建模的需要,在这个寒假期间小学了一下爬虫(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">hao123a>
<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'dafa\fafa/faf\fafaf\fasd\d\fx'
# 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')
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')
findTime = re.compile(r'(.*?)')
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)