爬虫(Python)
爬虫官网
Scrapy一目了然 — Scrapy 2.5.0 文档 (osgeo.cn)
快速跳转到函数声明
按住“Ctrl”

一、通过cmd新建爬虫项目
1、Windows下打开cmd
(1)方法一
常规方法打开开始菜单,运行(win+R)键。输入cmd


(2)方法二
开始菜单查找或者打开开始菜单,找到程序里面。可以找到 命令提示符
二、Windows下建立Scrapy项目
注意:python:进入Python编程环境;exit():退出编程环境
1、cmd中的程序上下关联
2、利用cmd运行.py文件
首先新建文本文档—修改文档为.py文件—cmd上直接输入文件名


1、cmd常用基本命令
1、列出所有任务及进程号,杀进程
tasklist
tasklist /? 获取使用帮助
taskkill
taskkill /? 获取使用帮助
2、cd 切换目录
cd /? //获取使用帮助
cd \ //跳转到硬盘的根目录
cd C:\WINDOWS //跳转到当前硬盘的其他文件
d: //跳转到其他硬盘
cd /d e:\software //跳转到其他硬盘的其他文件夹
注意此处必须加/d参数。否则无法跳转。
cd.. //跳转到上一层目录
3、cls :清除命令行
4、dir :显示目录中的文件和子目录列表。
5、tree 命令
tree:显示当前文件树形目录文件夹
tree/f:显示当前文件树形目录文件夹以及其他各类型文件
tree/?: //获取使用帮助
6、set path:查看系统变量
2、首先使用cmd命令转到想要建立项目的目录下,然后使用下面命令在该目录下建立项目
pip install Scrapy //下载爬虫
scrapy startproject Scrapy //新建爬虫项目框架



三、建立爬虫(spider)文件
scrapy genspider spiderFileName url
其中 spiderFileName为你所想创建的spider的文件名,url为你准备爬取的服务器域名,如 http://www.abc.com,然后可以在pycharm中进行编写了
1、示例
import scrapy
class QuotesSpider(scrapy.Spider):
name = 'quotes'
start_urls = [
'http://quotes.toscrape.com/tag/humor/',//网页的地址
]
def parse(self, response):
for quote in response.css('div.quote'):
yield {
'author': quote.xpath('span/small/text()').get(),
'text': quote.css('span.text::text').get(),
}
next_page = response.css('li.next a::attr("href")').get()
if next_page is not None:
yield response.follow(next_page, self.parse)
把它放在一个文本文件中,命名为 quotes_spider.py 然后用 runspider 命令:
scrapy runspider quotes_spider.py -o quotes.jl

四、爬虫伪装浏览器
1、原因
某些网站无法登录(418:已经被发现是爬虫)
# 打开网站,获取网站源码
response = urllib.request.urlopen("https://movie.douban.com/top250?start=0")
print(response.status)
# 418:已经被发现是爬虫
2、对获取到的网页源码进行utf-8解码
import urllib.request
# 对获取到的网页源码进行utf-8解码
response = urllib.request.urlopen("http://www.baidu.com")
print(response.read().decode('utf-8'))
将运行后的结果复制—粘贴到新建文本中(.html)—使用浏览器打开(相当于网站地址)


3、get、post请求(超时处理)
# 获取一个post请求
import urllib.parse
data = bytes(urllib.parse.urlencode({"hello": "world"}), encoding="utf-8")
response = urllib.request.urlopen("http://httpbin.org/post", data=data)
print(response.read().decode('utf-8'))
# 获取一个get请求(超时处理)
try:
response = urllib.request.urlopen("http://httpbin.org/get", timeout=0.01)
print(response.read().decode('utf-8'))
except urllib.error.URLError:
print("time out")
4、伪装成浏览器
(1)、F12打开源码—Network(网络)—F5刷新运行—找到“user-agent”



(2)注意:如果想要完整的伪装成浏览器,可以把headers的所有数据都放入程序中
import urllib.parse
url = "http://httpbin.org/post"
headers = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3947.100 Safari/537.36"
}
data = bytes(urllib.parse.urlencode({"name": "eric"}), encoding="utf-8")
req = urllib.request.Request(url=url, data=data, headers=headers, method="POST")
response = urllib.request.urlopen(req)
print(response.read().decode("utf-8"))
没有伪装的运行结果(user-agent)

伪装后的运行结果(user-agent)

(3)示例:访问豆瓣
import urllib.parse
url = "https://movie.douban.com/top250?"
# 用户代理,表示告诉服务器我们是什么类型的浏览器(本质上告诉浏览器我们可以接收什么水平的文件内容)
headers = { #模拟浏览器头部信息,向豆瓣服务器发送消息
"user-agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3947.100 Safari/537.36"
}
req = urllib.request.Request(url=url, headers=headers)
response = urllib.request.urlopen(req)
print(response.read().decode("utf-8"))
5、对伪装浏览器爬取的html文件进行解析
(1)BeautifulSoup库详解(将html文件转换为一个树形结构)
bs = BeautifulSoup(html, "html.parser") # 使用"html.parser"解析器解析html文件
(2)解析文件

# BeautifulSoup:将html文件转换为一个树形结构,每个节点都是Python对象,所有对象可以归纳为4种:
# - bs4.Tag:标签及其内容(拿到找到的第一个标签和内容)
# - bs4.NavigableString:内容(拿到找到的第一个内容)
# - BeautifulSoup:表示整个文档
# - bs4.Comment:一个特殊的NavigableString(输出的内容不包括注释符号)
import bs4
from bs4 import BeautifulSoup
# BeautifulSoup:将html文件转换为一个树形结构,每个节点都是Python对象,所有对象可以归纳为4种:
# - bs4.Tag:标签及其内容(拿到找到的第一个标签和内容)
# - bs4.NavigableString:内容(拿到找到的第一个内容)
# - BeautifulSoup:表示整个文档
# - bs4.Comment:一个特殊的NavigableString(输出的内容不包括注释符号)
file = open("D:\\VC\\c++\\Scrapy\\Scrapy\\baidu.html", "rb") # “rb":二进制读取文件
html = file.read().decode("utf-8")
bs = BeautifulSoup(html, "html.parser") # 使用"html.parser"解析器解析html文件
# print(bs.a) # <a class="toindex" href="/">百度首页</a>(拿到找到的第一个标签及其内容)
# print(type(bs.a)) # <class 'bs4.element.Tag'>
#
# print(bs.a.string) # 百度首页(只拿到内容)
# print(type(bs.a.string)) # <class 'bs4.element.Tag'>
#
# print(bs.a.attrs) # {'class': ['toindex'], 'href': '/'}(只拿到标签,以字典类型保存)
# print(type(bs.a.attrs)) # <class 'dict'>
# print(bs) # (表示整个文档)
# print(type(bs)) # <class 'bs4.BeautifulSoup'>
file.close()
(3)文档的遍历、搜索
import bs4
import re
from bs4 import BeautifulSoup
# BeautifulSoup:将html文件转换为一个树形结构,每个节点都是Python对象,所有对象可以归纳为4种:
# - bs4.Tag:标签及其内容(拿到找到的第一个标签和内容)
# - bs4.NavigableString:内容(拿到找到的第一个内容)
# - BeautifulSoup:表示整个文档
# - bs4.Comment:一个特殊的NavigableString(输出的内容不包括注释符号)
file = open("D:\\VC\\c++\\Scrapy\\Scrapy\\baidu.html", "rb") # “rb":二进制读取文件
html = file.read().decode("utf-8")
bs = BeautifulSoup(html, "html.parser") # 使用"html.parser"解析器解析html文件
# 文档的遍历(bs.head.contents:是一个列表类型)
# print(bs.head.contents[1])
# 文档的搜索
# (1)find_all(可以放规则或参数):会查找与字符串完全匹配的内容(只有”a“的内容)
# t_list = bs.find_all("a")
# for t in t_list:
# print(t)
# (2)正则表达式搜索,使用search()方法匹配内容(包含”a“的内容)
# t_list = bs.find_all(re.compile("a"))
# print(t_list)
# (3)参数方法keywords
# 注意:参数class要加'_'
# t_list = bs.find_all(class_="mnav c-font-normal c-color-t")
# for t in t_list:
# print(t)
# (4)参数方法text
# t_list=bs.find_all(text=["新闻", "地图", "贴吧"])
# t_list = bs.find_all(text=re.compile(r"\d")) # 应用正则表达式查找包含特定文本的内容(标签里的字符串)
# for t in t_list:
# print(t)
# (5)参数方法limit
# t_list = bs.find_all("a", limit=3, class_="mnav c-font-normal c-color-t")
# for t in t_list:
# print(t)
# <a class="mnav c-font-normal c-color-t" href="http://news.baidu.com" target="_blank">新闻</a>
# <a class="mnav c-font-normal c-color-t" href="https://www.hao123.com" target="_blank">hao123</a>
# <a class="mnav c-font-normal c-color-t" href="http://map.baidu.com" target="_blank">地图</a>
<a href="https://jobs.51job.com/chengdu-whq/137409226.html?s=sou_sou_soulb&t=0_0" target="_blank" class="el"><p class="t"><span title="Python开发工程师" class="jname at">Python开发工程师</span> <span class="time">01-29发布</span> <!----> <!----> <!----> <!----> <!----></p> <p class="info"><span class="sal">1-1.5万/月</span> <span class="d at">成都-武侯区 | 2年经验 | 本科 | 招2人</span></p> <p title="员工旅游 定期体检 节日福利 带薪年假 五险一金" class="tags"><span><!----> <i>员工旅游</i><i>定期体检</i><i>节日福利</i><i>带薪年假</i><i>五险一金</i></span></p></a>
# (6)css:选择器
# t_list = bs.select('title') # 通过标签查找 —— href:href
# t_list=bs.select(".mnav") # 通过类名查找 —— class:.el
# t_list=bs.select("#u1") # 通过id查找
# t_list=bs.select("a[href='http://news.baidu.com']") # 通过属性查找(标签a下面的href=""的元素)
# t_list=bs.select("head>title") # 通过子标签查找
# for t in t_list:
# print(t)
# file.close()
注意:文档搜索(3)中参数的选取

# <a class="mnav c-font-normal c-color-t" href="http://news.baidu.com" target="_blank">新闻</a>
# <a class="mnav c-font-normal c-color-t" href="https://www.hao123.com" target="_blank">hao123</a>
# <a class="mnav c-font-normal c-color-t" href="http://map.baidu.com" target="_blank">地图</a>
# <a class="mnav c-font-normal c-color-t" href="http://tieba.baidu.com/" target="_blank">贴吧</a>
# <a class="mnav c-font-normal c-color-t" href="https://haokan.baidu.com/?sfrom=baidu-top" target="_blank">视频</a>
# <a class="mnav c-font-normal c-color-t" href="http://image.baidu.com/" target="_blank"> 图片</a>
# <a class="mnav c-font-normal c-color-t" href="https://pan.baidu.com?from=1026962h" target="_blank">网盘</a>
6、正则表达式补充说明


() 的内容表示一个子表达式,() 本身不匹配任何字符,也不限制匹配任何字符,只是把括号内的内容作为同一个表达式来处理
例 (ab){1,3}:表示 ab 一起连续出现最少 1 次,最多 3 次
[] 表示匹配的字符在 [] 中,并且只能出现一次,并且特殊字符写在 [] 会被当成普通字符来匹配
例 [(a)]:匹配 ( 、 a 、 )这三个字符
| 函数 | 说明 |
| re.search() | 在一个字符串中搜索匹配正则表达式的第一个位置,返回match对象 |
| re.match() | m1 = re.search("asd", "wasd") # 前面的字符串是规则,后面的字符串是被校验的对象,返回match对象 |
| re.findall() | m = re.findall("[a-z]", "hSKLHjawdla") # 前面的字符串是规则,后面的字符串是被校验的对象 |
| re.split() | 将一个字符串按照正则表达式匹配结果进行分割,返回列表类型 |
| re.finditer() | 搜索字符串,返回一个匹配结果的迭代类型,每个迭代元素是match对象 |
| re.sub() | m = re.sub("a", "A", "ahdsqakj") # 找到a用大A替换 |

注意:建议在正则表达式中,被比较的字符串前面加上r,避免转义字符的问题

# 正则表达式:字符串模式(判断字符串是否符合一定的标准)
import re
# 创建模式对象
# 编译正则表达式模式,返回一个对象。可以把常用的正则表达式编译成正则表达式对象,方便后续调用及提高效率。
# pat = re.compile("AA")
# m = pat.search("wfsdsgAA") # search中的字符串是被校验的内容:<re.Match object; span=(6, 8)(左闭右开), match='AA'>
# print(m)
# 若没有模式对象
# m1 = re.search("asd", "wasd") # 前面的字符串是规则,后面的字符串是被校验的对象
# print(m1)
# findall()
# m = re.findall("[a-z]", "hSKLHjawdla") # 前面的字符串是规则,后面的字符串是被校验的对象
# print(m) # ['h', 'j', 'a', 'w', 'd', 'l', 'a'],返回找到的列表
# m = re.findall("[a-z]+", "hSKLHjawwwdla") # 前面的字符串是规则,后面的字符串是被校验的对象
# print(m) # ['h', 'jawwwdla'],返回找到的列表
# sub()
m = re.sub("a", "A", "ahdsqakj") # 找到a用大A替换
print(m) # AhdsqAkj
(1) pat = re.compile(r“”,re.S):设置正则表达式规则
(2) link = re.findall(pat, item)[0]:正则提取(第一个匹配的元素)
find = re.compile(r"(\d*)")
link = re.findall(find, "2SKJ3de23rd")
print(link)['2', '', '', '', '3', '', '', '23', '', '', '']
# -*- coding = utf-8 -*-
# @Time : 17:51
# @Author : huanhuan
# @File : test1.py
# @Software : PyCharm
import urllib.request
import bs4
import re
# 设置正则表达式规则
findlink = re.compile(r'<p class="">(.*?)</p>', re.S)
# 伪装浏览器获取网页数据
url = "https://movie.douban.com/top250?start=0"
header = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (# KHTML, like Gecko) Chrome/69.0.3947.100 Safari/537.36"
}
req = urllib.request.Request(url, headers=header, method="GET")
response = urllib.request.urlopen(req)
html = response.read().decode("utf-8")
# 使用"html.parser"解析器解析html文件
bs = bs4.BeautifulSoup(html, "html.parser")
data = []
# 找到想要的部分<div class="item" xpath="1"> :'div'是标签,soup.find_all():查找符合要求的字符串形成列表
for item in bs.find_all('div', class_="item"):
item = str(item)
# 正则提取
link = re.findall(findlink, item)[0]
# 对提取的数据进行改进
link = re.sub("<br(.*?)/(.*?)>", "", link)
link = re.sub("\xa0", "", link)
link = re.sub("\n", "", link)
link = re.sub(" ", "", link)
data.append(link.strip())
print(data)

7、Excel表格保存数据
(1)Excel表格存储
利用Python库xlwt将抽取的数据写入表格:以utf-8编码创建一个Excel对象—创建一个sheet表—往单元格写入内容—保存表格。
import xlwt
workbook = xlwt.Workbook(encoding="utf-8") # 创建Excel对象
work_sheet = workbook.add_sheet("sheet1") # 创建sheet表(参数是:sheet表名称)
work_sheet.write(0, 0, "hello") # 写入数据:第一个参数是“行”,第二个参数是“列”,第三个参数是内容
workbook.save("douban.xls") # 保存数据表(参数是:sheet表名称)

(2)示例:保存九九乘法表
import xlwt
workbook = xlwt.Workbook(encoding="utf-8") # 创建Excel对象
work_sheet = workbook.add_sheet("sheet1") # 创建sheet表(参数是:sheet表名称)
for i in range(1, 10):
for j in range(1, i + 1):
val = ("%d * %d = %d" % (j, i, j * i))
work_sheet.write(i-1, j-1, val) # 写入数据:第一个参数是“行”,第二个参数是“列”,第三个参数是内容
workbook.save("douban.xls") # 保存数据表(参数是:sheet表名称)

8、SQLite保存数据
(1)安装Database Navigator
打开pycharm,Ctrl + Alt + S 或点击File → setting → plugin(汉化版:文件→设置→插件),然后再在搜索框里输入Database Navigator 点击安装即可。

(2)导入(创建)数据:
# conn = sqlite3.connect("test.db") # 打开或创建数据库文件
# print("open data")
# c=conn.cursor()# 获取游标
# sql="""
# create table company
# (id int primary key null,
# name text not null,
# age int not null,
# address char(50),
# salary real);
# """
# c.execute(sql)#执行sql
# conn.commit()#提交数据库操作
# conn.close()#关闭数据库连接


打开表格(双击company):

(3)插入数据:(要先创建company文件,才能插入数据)
conn = sqlite3.connect("test.db") # 打开或创建数据库文件
print("open data")
c = conn.cursor() # 获取游标
sql1 = '''
insert into company(id,name,age,address,salary)
values(1,"张三",32,"北京",8000);
'''
# sql2 = '''
# insert into company (id,name,age,address,salary)
# values (1,"李四",32,"北京",8000);
# '''
c.execute(sql1) # 执行sql
# c.execute(sql2) # 执行sql
conn.commit() # 提交数据库操作
conn.close() # 关闭数据库连接
(4)显示数据:如果双击已创建的数据表并不显示已插入数据—弹出对话框后点击右下角红色圆圈里的Not Filter:



(5)查询数据
conn = sqlite3.connect("test.db") # 打开或创建数据库文件
print("open data")
c = conn.cursor() # 获取游标
sql = "select id,name,age,address,salary from company"
cursor = c.execute(sql) # 执行sql
for row in cursor:
print("id=", row[0], end="\t")
print("name=", row[1], end="\t")
print("age=", row[2], end="\t")
print("address=", row[3], end="\t")
print("salary=", row[4])
conn.commit() # 提交数据库操作
conn.close() # 关闭数据库连接
open data
id= 1 name= 张三 age= 32 address= 北京 salary= 8000.0
id= 2 name= 李四 age= 32 address= 北京 salary= 8000.0
(6)判断sql语句是否正确 (转到DDL—写入SQL语句—是否正确执行)



9、示例:爬取豆瓣电影
# -*- coding = utf-8 -*-
# @Time : 22:03
# @Author : huanhuan
# @File : test.py
# @Software : PyCharm
from bs4 import BeautifulSoup # 网页解析,获取数据
import re # 正则表达式,进行文字匹配
import xlwt # 进行Excel操作
import sqlite3 # 进行sqlite数据库操作
import urllib.request, urllib.error # 制作URL,获取数据
def main():
# 1、爬取网页
baseurl = "https://movie.douban.com/top250?start="
datalist = getData(baseurl)
# 2、逐一解析数据
# 3、保存数据
# 方法一:Excel保存
epath = "豆瓣电影Top250.xls"
saveData(datalist)
# 方法二:SQLite保存
spath = "movie.db"
saveDataDB(datalist, spath)
# ():把括号内的内容作为同一个表达式来处理,想要那个部分就用括号
# 影片详情链接的规则
FindLink = re.compile(r'<a href="(.*?)">') # 可以把常用的正则表达式编译成正则表达式对象,方便后续调用及提高效率。
# 影片图片链接的规则
FindImage = re.compile(r'<img.*src="(.*?)".*?/>', re.S) # re.S:忽略换行符
# 影片片名链接的规则
FindName = re.compile(r'<span class="title">(.*?)</span>', re.S) # re.S:忽略换行符
# 影片评分链接的规则
FindScore = re.compile(r'<span class="rating_num" property="v:average">(.*?)</span>', re.S) # re.S:忽略换行符
# 影片评价人数链接的规则
FindPeople = re.compile(r'<span>(.*?)人评价</span>', re.S) # re.S:忽略换行符
# 影片概况链接的规则
FindIntroduction = re.compile(r'<span class="inq">(.*?)</span>', re.S) # re.S:忽略换行符
# 影片相关内容的规则
FindRelatedContent = re.compile(r'<p class="">(.*?)</p>', re.S) # re.S:忽略换行符
# 1、爬取网页
def getData(baseurl):
datalist = []
for i in range(10):
url = baseurl + str(i * 25)
html = ask(url)
# 2、逐一解析数据
soup = BeautifulSoup(html, "html.parser")
for item in soup.find_all('div', class_="item"):
# 找到想要的部分<div class="item" xpath="1"> :'div'是标签
# soup.find_all():查找符合要求的字符串形成列表
# print(item) # 测试
data = []
item = str(item)
# 影片详情链接
link = re.findall(FindLink, item)[0] # 前面的字符串是规则,后面的字符串是被校验的对象
data.append(link)
# 影片图片
img = re.findall(FindImage, item)[0]
data.append(img)
# 影片片名
name = re.findall(FindName, item)
if len(name) == 2:
cname = name[0] # 添加中文名
data.append(cname)
ename = name[1].replace("\xa0", "") # 去掉无关的符号
ename = re.sub("/", "", ename) # 去掉无关的符号
data.append(ename) # 添加外文名
else:
data.append(name[0])
data.append(" ") # 外文名留空
# 影片评分
score = re.findall(FindScore, item)[0]
data.append(score)
# 影片评分人数
people = re.findall(FindPeople, item)[0]
data.append(people)
# 影片概况
introduction = re.findall(FindIntroduction, item)
if len(introduction) != 0:
introduction = introduction[0].replace("。", "。")
data.append(introduction)
else:
data.append(" ")
# 影片相关内容
related_content = re.findall(FindRelatedContent, item)[0]
related_content = re.sub("<br(.*?)/(.*?)>", "", related_content)
related_content = re.sub("\xa0", "", related_content)
related_content = re.sub("\n", "", related_content)
related_content = re.sub(" ", "", related_content)
data.append(related_content.strip())
datalist.append(data) # 处理好的一部电影信息放入datalist
# print(datalist)
return datalist
def ask(url):
head = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3947.100 Safari/537.36"
}
req = urllib.request.Request(url, headers=head)
try:
response = urllib.request.urlopen(req)
html = response.read().decode("utf-8")
# print(html)
except urllib.error.URLError as e:
if hasattr(e, "code"):
print(e.code)
if hasattr(e, "reason"):
print(e.reason)
return html
def saveData(datalist):
workbook = xlwt.Workbook(encoding="utf-8") # 创建Excel对象
work_sheet = workbook.add_sheet("豆瓣电影Top250", cell_overwrite_ok=True) # 创建sheet表(参数是:sheet表名称)
col = ('影片详情链接', '影片图片', '影片中文名', '影片外文名', '影片评分', '影片评分人数', '影片概况', '影片相关内容')
for i in range(0, 8):
work_sheet.write(0, i, col[i]) # 写入数据:第一个参数是“行”,第二个参数是“列”,第三个参数是内容
for i in range(0, 250):
print("第%d条" % i)
data = datalist[i]
for j in range(0, 8):
work_sheet.write(i + 1, j, data[j]) # 写入数据:第一个参数是“行”,第二个参数是“列”,第三个参数是内容
workbook.save("豆瓣电影Top250.xls") # 保存数据表(参数是:sheet表名称)
def saveDataDB(datalist, spath):
# 初始化创建表格
conn = sqlite3.connect(spath) # 打开或创建数据库文件
cursor = conn.cursor() # 获取游标
sql = '''
create table movie250
(
id integer primary key autoincrement,
info_link text,
pic_link text,
cname varchar,
ename varchar,
score numeric,
rated numeric,
introduction text,
info text
);
'''
cursor.execute(sql) # 执行sql
conn.commit() # 提交数据库操作
conn.close() # 关闭数据库连接
# 导入数据
conn = sqlite3.connect(spath) # 打开或创建数据库文件
cursor = conn.cursor() # 获取游标
for data in datalist:
for index in range(0, len(data)):
# 数值不需要加“”
# value中存放的元素类型:values (1,"李四",32,"北京",8000);
if index == 4 or index == 5:
continue
data[index] = '"' + str(data[index]) + '"'
sql = '''
insert into movie250(info_link,pic_link,cname,ename,score,rated,introduction,info)
values(%s)
''' % ",".join(data)
# print(sql)
cursor.execute(sql)
conn.commit() # 提交数据库操作
conn.close() # 关闭数据库连接
if __name__ == "__main__":
datalist = getData("https://movie.douban.com/top250?start=0")
# saveData(datalist=datalist)
saveDataDB(datalist, "movie.db")
def saveDataDB(datalist, spath)函数:导入数据部分的解析
(1)如果不将data【i】转为字符串类型:
# 导入数据
conn = sqlite3.connect(spath) # 打开或创建数据库文件
cursor = conn.cursor() # 获取游标
for data in datalist:
# for index in range(0, len(data)):
# if index == 4 or index == 5:
# continue
# data[index] = '"' + str(data[index]) + '"'
# value中存放的是字符串:values (1,"李四",32,"北京",8000);
print(data)
sql = '''
insert into movie250(info_link,pic_link,cname,ename,score,rated,introduction,info)
values(%s);
''' % ",".join(data)
print(sql)
['https://movie.douban.com/subject/1292052/', 'https://img2.doubanio.com/view/photo/s_ratio_poster/public/p480747492.jpg', '肖申克的救赎', 'The Shawshank Redemption', '9.7', '2533585', '希望让人自由。', '导演:弗兰克·德拉邦特FrankDarabont主演:蒂姆·罗宾斯TimRobbins/...1994/美国/犯罪剧情']
insert into movie250(info_link,pic_link,cname,ename,score,rated,introduction,info) values(https://movie.douban.com/subject/1292052/,https://img2.doubanio.com/view/photo/s_ratio_poster/public/p480747492.jpg,肖申克的救赎,The Shawshank Redemption,9.7,2533585,希望让人自由。,导演:弗兰克·德拉邦特FrankDarabont主演:蒂姆·罗宾斯TimRobbins/...1994/美国/犯罪剧情);
想要执行sql操作则要求values中的元素为字符串(带双引号)
(2)如果将data【i】转为字符串类型:(正确)
# 导入数据
conn = sqlite3.connect(spath) # 打开或创建数据库文件
cursor = conn.cursor() # 获取游标
for data in datalist:
for index in range(0, len(data)):
if index == 4 or index == 5:
continue
data[index] = '"' + str(data[index]) + '"'
# value中存放的是字符串:values (1,"李四",32,"北京",8000);
print(data)
sql = '''
insert into movie250(info_link,pic_link,cname,ename,score,rated,introduction,info)
values(%s);
''' % ",".join(data)
print(sql)
['"https://movie.douban.com/subject/1292052/"', '"https://img2.doubanio.com/view/photo/s_ratio_poster/public/p480747492.jpg"', '"肖申克的救赎"', '"The Shawshank Redemption"', '9.7', '2533604', '"希望让人自由。"', '"导演:弗兰克·德拉邦特FrankDarabont主演:蒂姆·罗宾斯TimRobbins/...1994/美国/犯罪剧情"']
insert into movie250(info_link,pic_link,cname,ename,score,rated,introduction,info) values("https://movie.douban.com/subject/1292052/","https://img2.doubanio.com/view/photo/s_ratio_poster/public/p480747492.jpg","肖申克的救赎","The Shawshank Redemption",9.7,2533604,"希望让人自由。","导演:弗兰克·德拉邦特FrankDarabont主演:蒂姆·罗宾斯TimRobbins/...1994/美国/犯罪剧情");
(3)不能将data【i】直接放入values中:values中放什么显示什么
# 导入数据
conn = sqlite3.connect(spath) # 打开或创建数据库文件
cursor = conn.cursor() # 获取游标
for data in datalist:
# for index in range(0, len(data)):
# if index == 4 or index == 5:
# continue
# data[index] = '"' + str(data[index]) + '"'
# value中存放的是字符串:values (1,"李四",32,"北京",8000);
print(data)
sql = '''
insert into movie250(info_link,pic_link,cname,ename,score,rated,introduction,info)
values(data[0],"data[1]","data[2]","data[3]","data[4]","data[5]","data[6]","data[7]");
''' #% ",".join(data)
print(sql)
['https://movie.douban.com/subject/1292052/', 'https://img2.doubanio.com/view/photo/s_ratio_poster/public/p480747492.jpg', '肖申克的救赎', 'The Shawshank Redemption', '9.7', '2533604', '希望让人自由。', '导演:弗兰克·德拉邦特FrankDarabont主演:蒂姆·罗宾斯TimRobbins/...1994/美国/犯罪剧情']
insert into movie250(info_link,pic_link,cname,ename,score,rated,introduction,info)
values(data[0],"data[1]","data[2]","data[3]","data[4]","data[5]","data[6]","data[7]");
五、数据可视化
1、flask介绍
(1)创建flask项目
新建项目—flask—创建



(2)示例(点击路由器地址—访问服务器)
from flask import Flask
app = Flask(__name__)
# 路由解析:通过用户访问的路径,匹配相应的函数
@app.route('/')
def hello_world(): # put application's code here
return '你好!'
if __name__ == '__main__':
app.run()


(3)开启debug模式(编辑配置—打勾):实时刷新网站显示



(4)通过访问路径,获取用户的字符串/整型参数
@app.route()括号里有什么,在访问路径时就要写什么才能访问到服务器
路由器路径不能重复,用户唯一路径访问他特定的函数
from flask import Flask
app = Flask(__name__)
# # 路由解析:通过用户访问的路径,匹配相应的函数
# @app.route('/')
# def hello_world(): # put application's code here
# return 'hello world!'
@app.route("/index")
def hello():
return "你好"
# 通过访问路径,获取用户的字符串参数
@app.route("/user/<name>") #@app.route()括号里有什么,在访问路径时就要写什么才能访问到服务器
def welcom(name):
return "你好,%s" % name
# 通过访问路径,获取用户的整型参数
@app.route("/user/<int:id>")
def welcom1(id):
return "你好,%d号会员" % id
if __name__ == '__main__':
app.run()




(5)路由解析:通过用户访问的路径,向页面传入.html
向template添加.html文件—利用函数访问
from flask import Flask, render_template
app = Flask(__name__)
# # 路由解析:通过用户访问的路径,匹配相应的函数
# @app.route('/')
# def hello_world(): # put application's code here
# return 'hello world!'
# 路由解析:通过用户访问的路径,匹配相应的.html
@app.route("/")
def index():
return render_template("baidu.html")
if __name__ == '__main__':
app.run()


(6)向页面传递变量
from flask import Flask, render_template
import datetime
app = Flask(__name__)
# # 路由解析:通过用户访问的路径,匹配相应的函数
# @app.route('/')
# def hello_world(): # put application's code here
# return 'hello world!'
# 向页面传递一个变量
@app.route("/")
def index():
time = datetime.date.today() # 普通变量
name = "焕焕" # 普通变量
name_list = ["小明", "小张", "小李"] # 列表变量
task = {"任务": "打扫卫生", "时间": "3小时"} # 字典变量
return render_template("index.html", var=time, var1=name, var2=name_list,task=task)
if __name__ == '__main__':
app.run()

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
今天是{{ var }},欢迎光临{{ var1 }}。<br/>
今天值班的有:<br/>
{% for data in var2 %}
<li>{{ data }}
{% endfor %}<br/>
任务:<br/>
<table border="1">
{% for key,value in task.items() %}
<tr>
<td>{{ key }}<td>
<td>{{ value }}<td>
</tr>
{% endfor %}
</table>
</body>
</html>

(7)了解如何在.html文件中打印表格,以及如何迭代
<br/>:换行符;
列表变量
{% for data in var2 %} #<!--用大括号和百分号括起来的是控制结构,还有if>
<li>{{ data }} <li>:换行
{% endfor %}<br/>
<table border="1"> 打印表格
<tr> <tr>:换行;<td>:换列
<td>测试1<td>
<td>测试2<td>
<tr>
<td>测试1<td>
<td>测试2<td>
<tr>
<td>测试1<td>
<td>测试2<td>
<tr>
</table>
Python 字典(Dictionary) items() 函数以列表返回可遍历的(键, 值) 元组数组。
任务:<br/>
<table border="1">
{% for key,value in task.items() %}
<tr>
<td>{{ key }}<td>
<td>{{ value }}<td>
</tr>
{% endfor %}
</table>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
今天是{{ var }},欢迎光临{{ var1 }}。<br/>
今天值班的有:<br/>
{% for data in var2 %}
<li>{{ data }}
{% endfor %}<br/>
任务:<br/>
<table border="1">
{% for key,value in task.items() %}
<tr>
<td>{{ key }}<td>
<td>{{ value }}<td>
</tr>
{% endfor %}
</table>
</body>
</html>

(8)表单提交(提交-接收)
th 元素内部的文本通常会呈现为居中的粗体文本,而 td 元素内的文本通常是左对齐的普通文本。
<th>{{ key }}</th>
<td>{{ value }}</td>
<form>标签用于定义表单域,以实现用户信息的收集和传递
表单域是一个包含表单元素的区域,包含以下部分:
▶ 表单域
▶ 表单控件(表单元素)
▶ 各控件的标示信息
表单域包含以下表单控件
▶ <input>标签,具体请参照<input>标签
▶ <select>标签,具体请参照<select>标签
▶ <textarea>标签,具体请参照<textarea>标签
▶ <label>标签,具体请参照<label>标签
■ 属性
□ action属性
定义:用于定义跳转的页面的URL地址值:URL地址,如XXXX/XXXX/xxx.php□ method属性
定义:用于定义页面提交的方式值:post,get等□ name属性
定义:用于定义表单域的名称值:自定义
register.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="{{ url_for('result') }}" method="post">
<p>姓名:<input type="text" name="姓名"></p>
<p>年龄:<input type="text" name="年龄"></p>
<p><input type="submit" value="提交"></p>
</form>
</body>
</html>
result.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<table border="1">
{% for key,value in result.items() %}
<tr>
<th>{{ key }}</th>
<td>{{ value }}</td>
</tr>
{% endfor %}
</table>
</body>
</html>
app.py
from flask import Flask, render_template, request
app = Flask(__name__)
# # 路由解析:通过用户访问的路径,匹配相应的函数
# @app.route('/')
# def hello_world(): # put application's code here
# return 'hello world!'
# 表单提交
@app.route("/test/register")
def register():
return render_template("test/register.html")
# 接收表单提交的路由需要特定method为post
@app.route("/result", methods=['POST', 'GET'])
def result():
if request.method == 'POST':
result = request.form
return render_template("test/result.html",result=result)
else:
return render_template("test/result.html")
if __name__ == '__main__':
app.run()



2、应用
(1)首页制作(下载网页模板—将assert、index.html导入flask)


(2)正确显示下载的网站模板(方法一:直接将index.html文件放入flask的static中,然后再移动到template中)
原模板

错误显示模板

方法二:将HTML文件中的assets改为static_assets
快捷键:Ctrl+c+r



(3)图标查询网站(iconfont-阿里巴巴矢量图标库)

(4)如何添加超链接
<a href="{{ movie[1] }}">
</a>给{{ movie[3] }}的值加上{{ movie[1] }}的超链接
<a href="{{ movie[1] }}">
{{ movie[3] }}
</a>
target="_blank":新打开一个网页
<a href="{{ movie[1] }}" target="_blank">
</a>

3、Echarts介绍
(1)官方网站
(2)快速应用(将下载保存 echarts.js 的目录新建一个 test.html 文件—打开路径—直接打开test.html文件)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>ECharts</title>
<!-- 引入刚刚下载的 ECharts 文件 -->
<script src="echarts.min.js"></script>
</head>
<body>
<!-- 为 ECharts 准备一个定义了宽高的 DOM -->
<div id="main" style="width: 600px;height:400px;"></div>
<script type="text/javascript">
// 基于准备好的dom,初始化echarts实例
var myChart = echarts.init(document.getElementById('main'));
// 指定图表的配置项和数据
var option = {
title: {
text: 'ECharts 入门示例'
},
tooltip: {},
legend: {
data: ['销量']
},
xAxis: {
data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
},
yAxis: {},
series: [
{
name: '销量',
type: 'bar',
data: [5, 20, 36, 10, 10, 20]
}
]
};
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option);
</script>
</body>
</html>




(3)如果想要改成其他图形(找到需要图形进入—复制代码—修改指定图表的配置项和数据到使用刚指定的配置项和数据显示图表之间的代码)


<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>ECharts</title>
<!-- 引入刚刚下载的 ECharts 文件 -->
<script src="echarts.min.js"></script>
</head>
<body>
<!-- 为 ECharts 准备一个定义了宽高的 DOM -->
<div id="main" style="width: 600px;height:400px;"></div>
<script type="text/javascript">
// 基于准备好的dom,初始化echarts实例
var myChart = echarts.init(document.getElementById('main'));
// 指定图表的配置项和数据
// prettier-ignore
let dataAxis = ['点', '击', '柱', '子', '或', '者', '两', '指', '在', '触', '屏', '上', '滑', '动', '能', '够', '自', '动', '缩', '放'];
// prettier-ignore
let data = [220, 182, 191, 234, 290, 330, 310, 123, 442, 321, 90, 149, 210, 122, 133, 334, 198, 123, 125, 220];
let yMax = 500;
let dataShadow = [];
for (let i = 0; i < data.length; i++) {
dataShadow.push(yMax);
}
option = {
title: {
text: '特性示例:渐变色 阴影 点击缩放',
subtext: 'Feature Sample: Gradient Color, Shadow, Click Zoom'
},
xAxis: {
data: dataAxis,
axisLabel: {
inside: true,
color: '#fff'
},
axisTick: {
show: false
},
axisLine: {
show: false
},
z: 10
},
yAxis: {
axisLine: {
show: false
},
axisTick: {
show: false
},
axisLabel: {
color: '#999'
}
},
dataZoom: [
{
type: 'inside'
}
],
series: [
{
type: 'bar',
showBackground: true,
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#83bff6' },
{ offset: 0.5, color: '#188df0' },
{ offset: 1, color: '#188df0' }
])
},
emphasis: {
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#2378f7' },
{ offset: 0.7, color: '#2378f7' },
{ offset: 1, color: '#83bff6' }
])
}
},
data: data
}
]
};
// Enable data zoom when user click bar.
const zoomSize = 6;
myChart.on('click', function (params) {
console.log(dataAxis[Math.max(params.dataIndex - zoomSize / 2, 0)]);
myChart.dispatchAction({
type: 'dataZoom',
startValue: dataAxis[Math.max(params.dataIndex - zoomSize / 2, 0)],
endValue:
dataAxis[Math.min(params.dataIndex + zoomSize / 2, data.length - 1)]
});
});
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option);
</script>
</body>
</html>


(4)显示电影评分和评分数量(要想在x轴显示字符串—加上|tojson)



@app.route('/score')
def score():
score1 = []
num = []
conn = sqlite3.connect("movie.db")
cursor = conn.cursor()
sql = '''select score,count(score) from movie250 group by score'''
data = cursor.execute(sql)
for item in data:
score1.append(str(item[0]))
num.append(item[1])
conn.commit()
conn.close()
return render_template("score.html", score=score1, num=num)
# return index()
score.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<!-- 引入刚刚下载的 ECharts 文件 -->
<script src="static/assets/js/echarts.min.js"></script>
<title>豆瓣top250数据可视化</title>
<meta content="" name="descriptison">
<meta content="" name="keywords">
<!-- Favicons -->
<link href="static/assets/img/favicon.png" rel="icon">
<link href="static/assets/img/apple-touch-icon.png" rel="apple-touch-icon">
<!-- Google Fonts -->
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300,300i,400,400i,600,600i,700,700i|Raleway:300,300i,400,400i,600,600i,700,700i,900" rel="stylesheet">
<!-- Vendor CSS Files -->
<link href="static/assets/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<link href="static/assets/vendor/icofont/icofont.min.css" rel="stylesheet">
<link href="static/assets/vendor/boxicons/css/boxicons.min.css" rel="stylesheet">
<link href="static/assets/vendor/animate.css/animate.min.css" rel="stylesheet">
<link href="static/assets/vendor/venobox/venobox.css" rel="stylesheet">
<link href="static/assets/vendor/aos/aos.css" rel="stylesheet">
<!-- Template Main CSS File -->
<link href="static/assets/css/style.css" rel="stylesheet">
</head>
<body>
<!-- ======= Header ======= -->
<header id="header">
<div class="container">
<div class="logo float-left">
<h1 class="text-light"><a href="temp.html"><span>Mamba</span></a></h1>
<!-- Uncomment below if you prefer to use an image logo -->
<!-- <a href="temp.html"><img src="static/assets/img/logo.png" alt="" class="img-fluid"></a>-->
</div>
<nav class="nav-menu float-right d-none d-lg-block">
<ul>
<li class="active"><a href="/">首页 <i class="la la-angle-down"></i></a></li>
<li><a href="/movie">电影</a></li>
<li><a href="/score">评分</a></li>
<li><a href="/word">词云</a></li>
<li><a href="/team">团队</a></li>
</ul>
</nav><!-- .nav-menu -->
</div>
</header><!-- End Header -->
<!-- ======= Our Team Section ======= -->
<section id="team" class="team">
<div class="container">
<div class="section-title">
<h2>豆瓣电影Top250评分分布图</h2>
</div>
<!-- ======= Counts Section ======= -->
<section class="counts section-bg">
<div class="container">
<!-- 为 ECharts 准备一个定义了宽高的 DOM -->
<div id="main" style="width: 100%;height:300px;"></div>
<script type="text/javascript">
// 基于准备好的dom,初始化echarts实例
var myChart = echarts.init(document.getElementById('main'));
// 指定图表的配置项和数据
option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
grid:{
left:'5%',
top:'10%',
right:'1%',
bottom:'10%'
},
color:('#3398DB'),
xAxis: {
type: 'category',
data: {{ score|tojson }}
<!--['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']-->
},
yAxis: {
type: 'value'
},
series: [
{
data: {{ num }},
<!--[120, 200, 150, 80, 70, 110, 130],-->
type: 'bar',
barWidth:'60',
showBackground: true,
backgroundStyle: {
color: 'rgba(180, 180, 180, 0.2)'
}
}
]
};
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option);
</script>
</div>
</section><!-- End Counts Section -->
</div>
</section><!-- End Our Team Section -->
<!-- ======= Footer ======= -->
<footer id="footer">
<div class="container">
<div class="copyright">
© Copyright <strong><span>Mamba</span></strong>. All Rights Reserved
</div>
</div>
</footer><!-- End Footer -->
<a href="#" class="back-to-top"><i class="icofont-simple-up"></i></a>
<!-- Vendor JS Files -->
<script src="static/assets/vendor/jquery/jquery.min.js"></script>
<script src="static/assets/vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
<script src="static/assets/vendor/jquery.easing/jquery.easing.min.js"></script>
<script src="static/assets/vendor/php-email-form/validate.js"></script>
<script src="static/assets/vendor/jquery-sticky/jquery.sticky.js"></script>
<script src="static/assets/vendor/venobox/venobox.min.js"></script>
<script src="static/assets/vendor/waypoints/jquery.waypoints.min.js"></script>
<script src="static/assets/vendor/counterup/counterup.min.js"></script>
<script src="static/assets/vendor/isotope-layout/isotope.pkgd.min.js"></script>
<script src="static/assets/vendor/aos/aos.js"></script>
<!-- Template Main JS File -->
<script src="static/assets/js/main.js"></script>
</body>
</html>
4、WordCloud介绍
(1)官网
API Reference — wordcloud 1.8.1 documentation
(2) 应用
# -*- coding = utf-8 -*-
# @Time : 22:04
# @Author : huanhuan
# @File : testcloud.py
# @Software : PyCharm
import jieba # 分词
from matplotlib import pyplot as plt # 绘图,数据可视化
from wordcloud import WordCloud # 词云
from PIL import Image # 图片处理
import numpy as np # 矩阵运算
import sqlite3 # 数据库
conn = sqlite3.connect("movie.db")
cursor = conn.cursor()
sql = '''select introduction from movie250'''
data = cursor.execute(sql)
text = ""
# item是元组,所以item[0]
for item in data:
item = list(item)
# 去除不需要的元素
item[0] = item[0].replace("的", "")
item[0] = item[0].replace("是", "")
item[0] = item[0].replace("你", "")
item[0] = item[0].replace("了", "")
item[0] = item[0].replace("就", "")
item[0] = item[0].replace("不", "")
item[0] = item[0].replace("人", "")
text += item[0]
conn.commit()
conn.close()
# 分词
cut = jieba.cut(text)
string = " ".join(cut)
print(len(string))
# 绘图
img = Image.open('./static/assets/img/tree.jpg') # 打开图片
img_array = np.array(img) # 将图片转换为数组
wc = WordCloud(
background_color='white',
mask=img_array,
font_path='msyh.ttc' # 字体
)
wc.generate_from_text(string)
# 图片处理
fig = plt.figure(1)
plt.imshow(wc)
plt.axis('off') # 是否显示坐标轴
plt.show() # 显示图片
# dpi:清晰度
plt.savefig('./static/assets/img/word.jpg',dpi=500)

(3) 数据库操作的数据是元组:不能直接使用data
conn = sqlite3.connect("movie.db")
cursor = conn.cursor()
sql = '''select introduction from movie250'''
data = cursor.execute(sql)
text = ""
# item是元组,所以item[0]
for item in data:
item = list(item)
# 去除不需要的元素
item[0] = item[0].replace("的", "")
item[0] = item[0].replace("是", "")
item[0] = item[0].replace("你", "")
item[0] = item[0].replace("了", "")
item[0] = item[0].replace("就", "")
item[0] = item[0].replace("不", "")
item[0] = item[0].replace("人", "")
text += item[0]
conn.commit()
conn.close()
(4) 词云的选取
img = Image.open('./static/assets/img/tree.jpg') # 打开图片
img_array = np.array(img) # 将图片转换为数组
wc = WordCloud(
background_color='white',#背景颜色
mask=img_array, #蒙版:必须是图片数组
font_path='msyh.ttc' # 字体(打开C:\Windows\Fonts—选取字体)
)
(5)注意:保存词云图片必须把plt.show()注释掉

5、总结
(1)爬取网页时要注意编码格式(content="text/html; charset=gbk"):因为charset=gbk,所以爬取网页时需要html = response.read().decode("gbk")



(2)爬取网页时把中文/外文转换为网页需要的字符(需要parse.quote(ky)两次)


# -*- coding = utf-8 -*-
# @Time : 13:40
# @Author : huanhuan
# @File : test.py
# @Software : PyCharm
from urllib import parse
keyword = parse.quote("大数据")
newkeyword = parse.quote(keyword)
print(newkeyword)
%25E5%25A4%25A7%25E6%2595%25B0%25E6%258D%25AE
(3) python 中最好用的身份证规则解析工具,地区码、性别、出生年月、身份证编码等快速校验!(from parseIdCard import parseIdCard)
python 中最好用的身份证规则解析工具,地区码、性别、出生年月、身份证编码等快速校验!_梦在硅谷的博客-CSDN博客
# -*- coding = utf-8 -*-
# @Time : 13:40
# @Author : huanhuan
# @File : test.py
# @Software : PyCharm
from parseIdCard import parseIdCard
area = parseIdCard.parseIdCard("340321199811066992")
print(area)
D:\ancod\envs\c++\python.exe D:/Python/文件/douban/test.py
{'code': 'OK', 'id': '34032119981106699', 'area': '安徽蚌埠市怀远县', 'age': 24, 'gender': '男', 'info': '身份证校验通过'}
(4)自定义访问网址 (url="https://search.51job.com/list/090200,000000,0000,00,9,99," + keyword + ",2,1.html?"):利用“+ +”可以拼接网站地址
def main():
ky = input("请输入你要搜索的岗位关键字:")
keyword = urllib.parse.quote(parse.quote(ky))
page_num = 1
url = "https://search.51job.com/list/090200,000000,0000,00,9,99," + keyword + ",2," + str(page_num) + ".html?"
# html = ask(url)
print(url)

(5)判断一个网页是否是动态网页
1、在网页源代码中查看href(超链接)属性,如果href = IP地址(类似:href="https://img3.doubanio.com/f/movie/d59b2715fdea4968a450ee5f6c95c7d7a2030065/pics/movie/apple-touch-icon.png"),这个子页面就是一个静态网页,如果href = 属性(类似:href="//js.51jobcdn.com"),这个子页面是一个动态网页(href = 属性,这个属性需要通过js加载,js可以让页面变成动态的页面,所以通过js加载的页面就是动态页面)
2、复制我们想要爬取的数据,在网页源代码中查找,如果在html中,这个页面就是静态页面,如果查找不到,说明我们想要的数据是通过js加载的,这个页面就是动态页面。通过Ajax加载的页面也是动态页面(动态页面的部分数据可能是静态的,所以判断的时候最好选取页面末尾的数据)
静态网页 :

动态网页:

(6)在读取HTML文件时:UnicodeDecodeError: 'gbk' codec can't decode byte 0xa4 in position 5304: illegal multibyte sequence
解法1(二进制读取): html=open('test.html','rb')
解法2(改变文件的编码方式): html=open('test.html',encoding='utf-8')
解法3: html=open('test.html',encoding='utf-8-sig')
(7)在爬取页面时:UnicodeDecodeError: 'gbk' codec can't decode byte 0xa4 in position 5304: illegal multibyte sequence
编码解码问题.decode(encoding='gbk', errors='ignore')_王轩的博客-CSDN博客_decode(gbk)

六、爬取动态页面
1、通过Selenium模拟浏览器抓取网站源码
使用浏览器渲染引擎。直接用浏览器在显示网页时解析HTML、应用CSS样式并执行JavaScript的语句。这个方法在爬虫过程中会打开一个浏览器加载该网页,自动操作浏览器浏览各个网页,顺便把数据抓下来。用一句简单而通俗的话说,就是使用浏览器渲染方法将爬取动态网页变成爬取静态网页。
(1)Selenium的安装与基本介绍
pip install selenium
或者直接安装库

(2)下载ChromeDriver,根据自己的chrome浏览器下载合适的版本
ChromeDriver - WebDriver for Chrome
from selenium import webdriver
driver=webdriver.Chrome()
driver.get('baidu.com')
或者
from selenium import webdriver
driver=webdriver.Chrome('D:\Python\文件\douban\chromedriver.exe')
driver.get('https://www.baidu.com/')
使用代码模拟浏览器打开https://www.baidu.com/,我们会发现会报错,这时因为在同文件文件路径中不存在
ChromeDriver.exe文件,webdriver.Chrome()函数的参数为ChromeDriver.exe文件的路径,我们必须把该文件复制到同路径下或者driver=webdriver.Chrome('D:\Python\文件\douban\chromedriver.exe')。


2、 selenium的基本操作
(1)访问/获取页面
想要定位并获取页面中的信息,首先要使用
webdriver打开指定页面,再去定位。
# import time
# from selenium import webdriver
# url='https://search.51job.com/list/090200,000000,0000,00,9,99,python,2,1.html'
# chrome=webdriver.Chrome() # 创建浏览器对象
# chrome.maximize_window() # 浏览器最大化
# chrome.get(url) # 打开该url对应的页面
# time.sleep(3) # 等待3s等待页面的加载
# html=chrome.page_source # 获取该url对应的页面源码
# print(html)
# # chrome.save_screenshot('./qq.png') #对打开的页面截图并保存
# chrome.close() # 关闭当前页面
# chrome.quit() # 退出当前页面
# chrome.back() # 回退
# chrome.forward() # 前进

浏览器打开 网站 会马上关闭,想要防止浏览器自动关闭,可以添加下面代码。
from selenium import webdriver
# 不自动关闭浏览器
option = webdriver.ChromeOptions()
option.add_experimental_option("detach", True)
# 注意此处添加了chrome_options参数
driver = webdriver.Chrome(chrome_options=option)
driver.get('https://www.csdn.net/')
html = driver.page_source # 获取该url对应的页面源码
print(html)
(2)页面元素定位方式(id 定位)
标签的 id 具有唯一性,就像人的身份证,假设有个 input 标签如下。
<input id="toolbar-search-input" autocomplete="off" type="text" value="" placeholder="C++难在哪里?">
我们可以通过 id 定位到它,由于 id 的唯一性,我们可以不用管其他的标签的内容。
driver.find_element_by_id("toolbar-search-input")
(3)页面元素定位方式(name 定位)
name 指定标签的名称,在页面中可以不唯一。假设有个 meta 标签如下
<meta name="keywords" content="CSDN博客,CSDN学院,CSDN论坛,CSDN直播">
我们可以使用 find_element_by_name 定位到 meta 标签。
driver.find_element_by_name("keywords")
(4)页面元素定位方式(class 定位)
class 指定标签的类名,在页面中可以不唯一。假设有个 div 标签如下
<div class="toolbar-search-container">
我们可以使用 find_element_by_class_name 定位到 div 标签。
driver.find_element_by_class_name("toolbar-search-container")
(5)页面元素定位方式(xpath 定位)
xpath 是一种在 XML 文档中定位元素的语言,它拥有多种定位方式,下面通过实例我们看一下它的几种使用方式。
<html>
<head>...<head/>
<body>
<div id="csdn-toolbar">
<div class="toolbar-inside">
<div class="toolbar-container">
<div class="toolbar-container-left">...</div>
<div class="toolbar-container-middle">
<div class="toolbar-search onlySearch">
<div class="toolbar-search-container">
<input id="toolbar-search-input" autocomplete="off" type="text" value="" placeholder="C++难在哪里?">
根据上面的标签需要定位 最后一行 input 标签,以下列出了四种方式,xpath 定位的方式多样并不唯一,使用时根据情况进行解析即可。
# 绝对路径(层级关系)定位
driver.find_element_by_xpath(
"/html/body/div/div/div/div[2]/div/div/input[1]")
# 利用元素属性定位
driver.find_element_by_xpath(
"//*[@id='toolbar-search-input']"))
# 层级+元素属性定位
driver.find_element_by_xpath(
"//div[@id='csdn-toolbar']/div/div/div[2]/div/div/input[1]")
# 逻辑运算符定位
driver.find_element_by_xpath(
"//*[@id='toolbar-search-input' and @autocomplete='off']")
(6)页面元素定位方式(css 定位)
CSS 使用选择器来为页面元素绑定属性,它可以较为灵活的选择控件的任意属性,一般定位速度比 xpath 要快,但使用起来略有难度。CSS 选择器常见语法:

举个简单的例子,同样定位上面实例中的 input 标签。
driver.find_element_by_css_selector('#toolbar-search-input')
driver.find_element_by_css_selector('html>body>div>div>div>div>div>div>input')
(7)页面元素定位方式(link 定位)
link 专门用来定位文本链接,假如要定位下面这一标签。
<div class="practice-box" data-v-04f46969="">加入!每日一练</div>
我们使用 find_element_by_link_text 并指明标签内全部文本即可定位。
driver.find_element_by_link_text("加入!每日一练")
(8)页面元素定位方式(partial_link 定位)
partial_link 翻译过来就是“部分链接”,对于有些文本很长,这时候就可以只指定部分文本即可定位,同样使用刚才的例子。
<div class="practice-box" data-v-04f46969="">加入!每日一练</div>
我们使用 find_element_by_partial_link_text 并指明标签内部分文本进行定位
driver.find_element_by_partial_link_text("加入")
(9)定位多个元素
- find_element() : 返回WebElement对象
- find_elements():返回列表对象,列表中的每个元素都是一个WebElement对象
3、页面元素定位返回WebElement对象,对WebElement进行操作
(1)WebElement.text—返回WebElement对象中所有文字(文本)
import re
import urllib.request
from urllib import request
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
import bs4
from pyquery import PyQuery as pq
def main():
# url = 'https://movie.douban.com/typerank?type_name=%E5%8A%A8%E7%94%BB&type=25&interval_id=100:90'
baseurl = "https://movie.douban.com/top250?start="
# 不自动关闭浏览器
option = webdriver.ChromeOptions()
option.add_experimental_option("detach", True)
chrome = webdriver.Chrome(options=option)
chrome.maximize_window()
chrome.get(baseurl) # 打开该url对应的页面
time.sleep(2) # 等待2s等待页面的加载
html = chrome.page_source # 获取该url对应的页面源码
name = chrome.find_element(By.XPATH, "(//h1[contains(text(),'豆瓣电影 Top 250')])[1]")
print(name.text)
print(chrome.find_element(By.CSS_SELECTOR,"div[id='content'] h1").text)
print(chrome.find_element(By.CSS_SELECTOR,"div[id='content'] h1"))
chrome.close()

(2)其他操作
size 获取元素的尺寸
text 获取元素的文本
get_attribute(name) 获取属性值:
location 获取元素坐标,先找到要获取的元素,再调用该方法
page_source 返回页面源码
driver.title 返回页面标题
current_url 获取当前页面的URL
is_displayed() 设置该元素是否可见
is_enabled() 判断元素是否被使用
is_selected() 判断元素是否被选中
tag_name 返回元素的tagName
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time
driver = webdriver.PhantomJS(executable_path="G:\phantomjs-1.9.1-windows\phantomjs.exe")
driver.get("http://www.baidu.com/")
size = driver.find_element_by_name("wd").size
print size
#尺寸: {'width': 500, 'height': 22}
news = driver.find_element_by_xpath("//div[@id='u1']/a[1]").text
print news
#文本: 新闻
href = driver.find_element_by_xpath("//div[@id='u1']/a[2]").get_attribute('href')
name = driver.find_element_by_xpath("//div[@id='u1']/a[2]").get_attribute('name')
print href,name
#属性值: http://www.hao123.com/ tj_trhao123
location = driver.find_element_by_xpath("//div[@id='u1']/a[3]").location
print location
#坐标: {'y': 19, 'x': 498}
print driver.current_url
#当前链接: https://www.baidu.com/
print driver.title
#标题: 百度一下, 你就知道
result = location = driver.find_element_by_id("su").is_displayed()
print result
#是否可见: True

4、元素的交互操作
(1)send_keys() 方法—输入文字
# 先定位到输入框
input_tag = driver.find_element(By.ID,'abc')
input_tag.send_keys('输入文字')
import re
import urllib.request
from urllib import request
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
import bs4
from pyquery import PyQuery as pq
def main():
# url = 'https://movie.douban.com/typerank?type_name=%E5%8A%A8%E7%94%BB&type=25&interval_id=100:90'
baseurl = "https://movie.douban.com/top250?start="
# 不自动关闭浏览器
option = webdriver.ChromeOptions()
option.add_experimental_option("detach", True)
chrome = webdriver.Chrome(options=option)
chrome.maximize_window()
chrome.get(baseurl) # 打开该url对应的页面
time.sleep(2) # 等待2s等待页面的加载
html = chrome.page_source # 获取该url对应的页面源码
input_tag = chrome.find_element(By.CSS_SELECTOR, "#inp-query")
input_tag.send_keys("肖申克的救赎")

(2)clear() 方法—清空文字
# 先定位到输入框
input_tag= driver.find_element(By.ID,'abc')
input_tag.clear()
(3) click() 方法—点击按钮
# 先定位到按钮
button = driver.find_element(By.ID,'abc')
button.click()
import re
import urllib.request
from urllib import request
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
import bs4
from pyquery import PyQuery as pq
def main():
# url = 'https://movie.douban.com/typerank?type_name=%E5%8A%A8%E7%94%BB&type=25&interval_id=100:90'
baseurl = "https://movie.douban.com/top250?start="
# 不自动关闭浏览器
option = webdriver.ChromeOptions()
option.add_experimental_option("detach", True)
chrome = webdriver.Chrome(options=option)
chrome.maximize_window()
chrome.get(baseurl) # 打开该url对应的页面
time.sleep(2) # 等待2s等待页面的加载
html = chrome.page_source # 获取该url对应的页面源码
#找到搜索框
input_tag = chrome.find_element(By.CSS_SELECTOR, "#inp-query")
input_tag.send_keys("肖申克的救赎")
#找到按钮
button = chrome.find_element(By.CSS_SELECTOR, 'input[value="搜索"]')
button.click()

5、Action Chains类
相关方法和属性
动作链,实现指定的一些列动作,点击,输入,拖拽,键盘按键等。
当实例化一个ActionChains类后,定义的一系列动作被存储在ActionChains对象的队列中,当调用perform()方法时,这些动作按照队列顺序执行。
(1) click()
点击指定元素,如果没指定,在鼠标当前位置点击
click(on_element=None)#on_element:要点击的元素
(2) click_and_hold()
在元素上按住左键,如果没指定,在鼠标当前位置按住左键
click_and_hold( on_element=None )
on_element:鼠标按下的元素
(3) context_click()
右键点击元素,如果没指定,在鼠标当前位置右击
context_click(on_element=None)
on_element:鼠标右击的元素
(4) double_click()
双击元素,如果没指定,在鼠标当前位置双击
double_click(on_element=None)
on_element:鼠标双击的元素
(5) drag_and_drop()
在source上按住左键,拖动至target处释放左键
drag_and_drop(source, target)
source:被拖动的元素,一个element对象
target:拖动到指定元素的位置,另一个element对象
(6) drag_and_drop_by_offset()
指定偏移量拖拽
drag_and_drop_by_offset(source, xoffset, yoffset)
source:被拖动的元素,一个element对象
xoffset:x偏移量
yoffset:y偏移量
(7) key_down()
键盘操作,只能操作Control, Alt 和Shift三个键,与这三个键相组合的键用send_keys()发送。该方法只按下按键,不释放。
from selenium.webdriver.common.keys import Keys
key_down(value, element=None)
value:Keys.CONTROL、Keys.ALT、Keys.SHIFT,值定义在Keys类中
element:要操作的元素,如果每指定,则操作当前聚焦的元素
比如:按下ctrl+c
ActionChains(driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()
(8) key_up()
释放键盘
key_up(value, element=None)
value:释放的键,同key_down()
element:同key_down()
(9) move_to_element()
移动鼠标到一个元素的中央
move_to_element(to_element)
to_element:目标元素
(10) move_by_offset()
相对于当前鼠标位置移动一个偏移量
move_by_offset(xoffset, yoffset)
xoffset:x偏移量
yoffset:y偏移量
(11) move_to_element_with_offset()
移动鼠标到偏倚目标元素左上角指定偏移量的位置
move_to_element_with_offset(to_element, xoffset, yoffset)
to_element:目标元素
xoffset:x偏移量
yoffset:y偏移量
(12) pause()
暂停输入
pause(seconds)
seconds:暂停的秒数
(13) perform()
执行队列中的所有动作
(14) release()
释放元素上按下的鼠标
release(on_element=None)
on_element:目标元素
(15) reset_actions()
清空队列中的所有动作
6、执行JS处理滚动条
有时候我们需要控制页面上的滚动条,但是滚动条并非页面上的元素,无法定位和操作,这时候就需要调用js来处理。
一般用到操作滚动条的会两个场景:
要操作的页面元素不在当前页面范围,无法进行操作,需要拖动滚动条
注册时的法律条文需要阅读,判断用户是否阅读的标准是:滚动条是否拉到最下方。
(1)通过 x ,y 坐标滑动
对于这种通过坐标滑动的方法,我们需要知道做表的起始位置在页面左上角(0,0),下面看一下示例,滑动 CSDN 首页。
from selenium import webdriver
from time import sleep
driver = webdriver.Chrome()
driver.get("https://blog.csdn.net/")
sleep(1)
js = "window.scrollTo(0,500);"
driver.execute_script(js)

(2)通过参照标签滑动
这种方式需要先找一个参照标签,然后将滚动条滑动至该标签的位置。下面还是用 CSDN 首页做示例,我们用循环来实现重复滑动。该
li标签实际是一种懒加载,当用户滑动至最后标签时,才会加载后面的数据。
7、options参数设置
(1)无头模式
测试代码时,每次运行程序打开浏览器有助于我们观察,可是如果之后每次获取数据的时候都自动的打开浏览器,不仅会影响心情,还会占用资源。所以,我们可以添加选项来避免这种情况
option = webdriver.ChromeOptions()
option.add_argument('--headless')
(2)设置代理
option.add_argument("--proxy-server=http://61.138.33.20:808")
8、窗口切换
在
selenium操作页面的时候,可能会因为点击某个链接而跳转到一个新的页面(打开了一个新标签页),这时候selenium实际还是处于上一个页面的,需要我们进行切换才能够定位最新页面上的元素。窗口切换需要使用
switch_to.windows()方法:driver.execute_script('window.open(url)'):打开新的选项卡(窗口),地址为url;
driver.switch_to.window(driver.window_handles[i]):转到第i个窗口;
from selenium import webdriver
handles = []
driver = webdriver.Chrome()
driver.get('https://blog.csdn.net/')
# 设置隐式等待
driver.implicitly_wait(3)
# 获取当前窗口的句柄
handles.append(driver.current_window_handle)
# 点击 python,进入分类页面
driver.find_element_by_xpath('//*[@id="mainContent"]/aside/div[1]/div').click()
# 切换窗口
driver.switch_to.window(driver.window_handles[-1])
# 获取当前窗口的句柄
handles.append(driver.current_window_handle)
print(handles)
print(driver.window_handles)
上面代码在点击跳转后,使用 switch_to 切换窗口,window_handles 返回的 handle 列表是按照页面出现时间进行排序的,最新打开的页面肯定是最后一个,这样用 driver.window_handles[-1] + switch_to 即可跳转到最新打开的页面了。
9、反检测
(1)selenium隐藏指纹特征
selenium 对于部分网站来说十分强大,但它也不是万能的,实际上,selenium 启动的浏览器,有几十个特征可以被网站检测到,轻松的识别出你是爬虫。
不相信?接着往下看,首先你手动打开浏览器输入https://bot.sannysoft.com/,在网络无异常的情况下,显示应该如下:

下面通过 selenium 来打开浏览器。
from selenium import webdriver
driver = webdriver.Chrome()
driver.get('https://bot.sannysoft.com/')
通过 webdriver:present 可以看到浏览器已经识别出了你是爬虫,我们再试一下无头浏览器。

(2)解决方法
没错,就是这么真实,对于常规网站可能没什么反爬,但真正想要抓你还是一抓一个准的。
说了这么多,是不是 selenium 真的不行?别着急,实际还是解决方法的。关键点在于如何在浏览器检测之前将这些特征进行隐藏,事实上,前人已经为我们铺好了路,解决这个问题的关键,实际就是一个 stealth.min.js 文件,这个文件是给 puppeteer 用的,在 Python 中使用的话需要单独执行这个文件,该文件获取方式需要安装 node.js ,如果已安装的读者可以直接运行如下命令即可在当前目录生成该文件;
下面我们在网站检测之前先执行该js文件隐藏特征,同样使用无头浏览器,看是否有效。
import time
from selenium.webdriver import Chrome
option = webdriver.ChromeOptions()
option.add_argument("--headless")
# 无头浏览器需要添加user-agent来隐藏特征
option.add_argument(
'user-agent=Mozilla/5.0 (Windows NT 10.0; Win64;x64)AppleWebKit/537.36(KHTML,like Gecko) Chrome/97.0.4692.99 Safari/537.36')
driver = Chrome(options=option)
driver.implicitly_wait(5)
with open('stealth.min.js') as f:
js = f.read()
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
"source": js
})
driver.get('https://bot.sannysoft.com/')
driver.save_screenshot('hidden_features.png')
通过 stealth.min.js 的隐藏,可以看到这次使用无头浏览器特征基本都以隐藏,已经十分接近人工打开浏览器了。
10、cookies操作
cookies 是识别用户登录与否的关键,爬虫中常常使用 selenium + requests 实现 cookie持久化,即先用 selenium 模拟登陆获取 cookie ,再通过 requests 携带 cookie 进行请求。
webdriver 提供 cookies 的几种操作:读取、添加删除。
get_cookies:以字典的形式返回当前会话中可见的 cookie 信息。
get_cookie(name):返回 cookie 字典中 key == name 的 cookie 信息。
add_cookie(cookie_dict):将 cookie 添加到当前会话中
delete_cookie(name):删除指定名称的单个 cookie。
delete_all_cookies():删除会话范围内的所有 cookie。
11、注意
(1)在使用selenium页面元素定位方式时,发现终端会给出这样的提示:
DeprecationWarning: find_element_by_* commands are deprecated. Please use find_element() instead
说明开发者不建议我们用上述的方法,推荐我们用find_element()和find_elements()方法来代替。其实换汤不换药,下面来看看用法:
# 首先要引入By类
from selenium.webdriver.common.by import By
driver.find_element(By.ID,'su')
driver.find_element(By.XPATH,'xpath')
ID = "id"
XPATH = "xpath"
LINK_TEXT = "link text"
PARTIAL_LINK_TEXT = "partial link text"
NAME = "name"
TAG_NAME = "tag name"
CLASS_NAME = "class name"
CSS_SELECTOR = "css selector"
(2)css-selector (selenium或bs4.BeautifulSoup)的使用方法
1、data=driver.find_elements(By.CSS_SELECTOR,'.j_joblist>.e>.el>.t>span[title]')
注意:无论是selenium或bs4.BeautifulSoup中的css:选择器,在查找元素时都需要定位到该元素的所在<>中
# css:选择器
# t_list = bs.select('title') # 通过标签查找 —— href:href
# t_list=bs.select(".mnav") # 通过类名查找 —— class:.el
# t_list=bs.select("#u1") # 通过id查找
# t_list=bs.select("a[href='http://news.baidu.com']") # 通过属性查找(标签a下面的href=""的元素)
# t_list=bs.select("head>title") # 通过子标签查找2、联合使用
.e>:表示接下来搜索类e中的元素;
>span[title]:找到标签span中的title属性
3、页面元素定位返回WebElement对象,对WebElement进行操作
data = driver.find_elements(By.CSS_SELECTOR, '.j_joblist>.e>a')
data.get_attribute('href')
4、WebElement的操作
WebElement.size 获取元素的尺寸
WebElement.text 获取元素的文本
WebElement.get_attribute(name) 获取属性值:
WebElement.location 获取元素坐标,先找到要获取的元素,再调用该方法
driver.page_source 返回页面源码
driver.title 返回页面标题
driver.current_url 获取当前页面的URL
is_displayed() 设置该元素是否可见
is_enabled() 判断元素是否被使用
is_selected() 判断元素是否被选中
tag_name 返回元素的tagName
import re
import urllib.request
from urllib import request
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
import bs4
from pyquery import PyQuery as pq
def main():
url = 'https://search.51job.com/list/090200,000000,0000,00,9,99,python,2,1.html?'
link = chrome_ask(url)
print(link)
def chrome_ask(url):
option = webdriver.ChromeOptions()
# option.add_argument("--headless")
option.add_experimental_option("detach", True)
# 无头浏览器需要添加user-agent来隐藏特征
option.add_argument(
'user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36')
driver = webdriver.Chrome(options=option)
driver.maximize_window()
driver.implicitly_wait(5)
with open('stealth.min.js') as f:
js = f.read()
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
"source": js
})
driver.get(url)
time.sleep(1)
html = driver.page_source
link_list=[]
data_list = driver.find_elements(By.CSS_SELECTOR, '.j_joblist>.e>a')
for data in data_list:
link_list.append(data.get_attribute('href'))
driver.close()
return link_list


更多推荐


所有评论(0)