[B站视频]Python爬虫技术5天速成

课程介绍

B站课程链接
学习主题:Python爬虫和数据可视化
学习内容:
(1)Python语言的基础知识
(2)网络爬虫的技术实现
(3)数据可视化的技术应用(框架、组件等)
学习目标: 了解网络爬虫和数据可视化的技术原理与流程

1.Python基础概述

Python诞生和发展
1991年,第一个Python编译器(同时也是解释器)(可以解析你的指令了)诞生,它用C语言实现,并能调用C库。
2000年,Python2.0发布。
2008年,Python3发布。
基本介绍
Python是一门解释型(JAVA,C#不一样,需要先编译成一个中间状态,再运行中间状态文件,生成一个运行文件,而Python是写完之后直接可以执行的解释型语言)、面向对象(和JAVA一样,具有封装,继承,多态的功能)的高级编程语言。
Python是开源免费的、支持交互式、可跨平台移植的脚本语言。
Python的设计混合了传统语言的软件工程的特点和脚本语言的易用性,具有如下特性
1.开源、易于维护
2.可移植
3.易于使用、简单优雅
4.广泛的标准库、功能强大
5.可扩展、可嵌入
6. …
Python存在的缺点:
1.运行速度慢:Python是解释型语言,边执行需要边翻译成机器码,耗时,而C语言则在运行前直接编译成CPU能执行的机器码。但大量应用程序不需要那么快的运行速度,因为用户根本感觉不出来
2.代码不能加密:解释型语言发布程序就是发布源代码,而C语言只需要把编译后的机器码发布出去,从机器码反推出C代码是不可能的。
Python典型应用
在这里插入图片描述
在这里插入图片描述
1.3 第一个Python程序
在命令行/终端输入python或者python3进入python环境
退出python环境的方法:
1.用exit()
2.按Ctrl+Z(遇到死循环也可以用这种方法退出)

如何在命令行/终端直接执行python文件:
在命令行/终端直接输入

python <py文件名.py>

python3 <py文件名.py>

1.5 变量及类型
赋值(比如a=‘ABC’)时,Python解释器干了两件事:
1.在内存中创建一个‘ABC’的字符串
2.在内存中创建一个名为a的变量,并把它指向‘ABC’

1.6 标识符和关键字
什么是关键字
是python的一些具有特殊功能的标志符,这就是所谓的关键字,他们是python已经使用的,所以不允许开发者自己定义和关键字相同的名字的标志符
查看关键字代码:

import keyword
keyword.kwlist

返回结果是:

and	as 	assert	break	class	continue	def	del
elif	else	except	exec	finally	for	from	global
if	in	import	is	lambda	not	or	pass
print	raise	return	try	while	with	yield

1.7 输出
格式化输出:

age=18
print('这是我的年纪:%d岁'%age)

%d,格式化的是有符号十进制整数,%s也比较常用,格式化的是字符串

print('我的名字是%s,我的国籍是%s'%('小张','中国'))

补充:可以通过sep=’.'表示通过.将前面的字符串连接起来,如

print('www','baidu','com',sep='.')

返回结果是www.baidu.com
观察以下代码返回结果(功能已在代码注释中):

print('hello', end='')	  # 打印之后不空格直接跟下一个内容
print('world', end='\t')  # 打印之后自动tab占位
print('python', end='\n') # 打印之后自动换行
print('end')			  # 不对end参数赋值,则默认end='\n'

结果:

helloworld	python
python
end

1.8 输入

password = input('请输入密码:') 
print('您刚刚输入的密码是:',password)
print(type(password)) # 即使输入的是数字,其实password存的还是字符串

注意: input输入得到的是字符串,所以后续进行print输出应该对应%s而不是%d
如果需要把字符串转换成整型,就需要使用强制类型转换int()

1.9 运算符和表达式
重点注意:假设a=10,b=21
:幂-返回x的y次幂,ab为10的21次方
//:取整除-返回向下取接近除数的整数,9//2的结果是4,-9//2的结果是-5

补充: 随机数模块

import random # 引入随机库
x = random.randint(0, 2) # 随机生成[0,2]的随机数,包含0、1、2
print(x)
### import与from...import
在python中用import或者from...import来导入相应的模块
将整个模块(somemodule)导入,格式为:import somemodule
从某个模块中导入某个函数,格式为:from somemodule import somefunction
从某个模块中导入多个函数,格式为:from somemodule import firstfunc, secondfunc, thirdfunc
将某个模块中的全部函数导入,格式为:from somemodule import \*

课堂作业1:IF语句实现石头剪子布

import random
game_1 = input('请输入:剪刀(0)、石头(1)、布(2):') # 注意input里面的引号不要忘记
if game_1 not in ['0','1','2']: # 因为input赋予变量的类型是字符串,所以必须考虑not in['0','1','2']
    print('你输入的不是0,1,2中的任何一个')
else:
    if game_1 == 0:
        print('你输入为:剪刀(0)')
    elif game_1 == 1:
        print('你输入为:石头(1)')
    elif game_1 == 2:
        print('你输入为:布(2)')
    game_2 = random.randint(0,2)
    print('随机生成数字为:%d'%game_2)
    if (game_1==0 and game_2==2) or (game_1==1 and game_2==0) or (game_1==2 and game_2==1):
        print('啊,你赢了')
    elif game_1==game_2:
        print('哈哈,平局')
    else:
        print('哈哈,你输了:)')

补充: python可以用isinstance()函数来判断一个变量是否是某个类型,如

isinstance(a, int) # 判断变量a是否是int类型,是则返回TRUE,否则返回FALSE

课堂作业2:用For和While循环打印九九乘法表

for i in range(1,10):
    for j in range(1,i+1):
        print('%d*%d=%d'%(i,j,i*j),end='\t')
    print()

补充: r取消转义

print('hello\nchengdu') # 使用反斜杠,实现转义字符的功能
print(r'hello\nchengdu') # 在字符串前面加r,表示直接显示原始字符串,不进行转义

字符串常见操作(只介绍部分常用到的)

bytes.decode(encoding='utf-8',error='strict')

1.python3中没有decode方法,但我们可以使用bytes对象的decode()来解码给定的bytes对象,这个bytes对象可以由str.encode()来编码返回。

encode(encoding='utf-8',errors='strict')

2.以encoding指定的编码格式编码字符串,如果出错默认报一个ValueError的异常,除非errors指定的是‘ignore’或者‘replace’

str.isalnum()

3.如果字符串至少有一个字符并且所有字符都是字母或数字则返回True,否则返回False

str.isalpha()

4.如果字符串至少有一个字符并且所有字符都是字母,则返回True,否则返回False

str.isdigit()

5.如果字符串只包含数字,则返回True,否则返回False

str.isnumeric()

6.如果字符串只包含数字字符,则返回True,否则返回False(注意和isdigit()是有区别的,可以搜索相关博客来了解)

str.join(seq)

7.以指定字符串seq为分隔符,将str对象的所有的元素(的字符串表示)合并成一个新的字符串

len(str)

8.返回字符串长度

str.lstrip([chars])

9.截掉字符串左边的指定字符,chars为指定截取的字符,默认为空格

str.rstrip([chars])

10.删除 string 字符串末尾的指定字符,chars为指定截取的字符,默认为空格

str.split(str="", num=string.count(str))

11.以str为分隔符截取字符串,如果num有指定值,则仅截取num+1个字符串

3.2列表

在这里插入图片描述
补充:

list.index(obj,start,stop)

从list中的start开始到stop结束(不包括stop)去查找obj,查到则返回下标,查不到则报错

list.count(obj)

在list中统计obj对象有多少个,并返回

课堂作业3

products = [['iphone',6888],['MacPro',14800],['小米6',2499],['Coffee',31],['Book',60],['Nike',699]]
print('------ 商品列表 ------')
for i in range(len(products)):
    print('%d  %-5s\t%d'%(i,products[i][0],products[i][1])) # -5表示左对齐,且对应的字符串固定占用5个字符位

store = []
while True:
    buy = input('请问您需要购买什么?请输入产品编号0-5(若完成购买则输入\'q\'退出):')
    if buy == 'q':
        break
    else:
        store.append(int(buy)) # 因为input得到的是字符串,所以需要int强制类型转换

print('------ 购买的商品列表 ------')
total  = 0
store.sort() # 排序,为了让下面的列表好看一些
for index in store:
    value = products[index][1]
    print('%d  %-5s\t%d'%(index, products[index][0], value))
    total += value

print('购买的商品总数为:',len(store))
print('购买的商品总价为:',total)

3.3元组

Tuple(元组)
元组的元素不可变,但可以包含可变对象,如list。
注意:定义一个只有1个元素的tuple,必须加逗号,如果不加逗号,则得到的并不是一个元组,而是本身小括号里面元素的类型。

3.4字典

字典的访问方式:

info = {'name': '吴彦祖','age':18}
print(info['name']) # 普通的直接访问
print(info['gender']) # 普通的直接访问,没找到会会报错
print(info.get('gender')) # 使用get方法,没有找到对应的键,默认返回None
print(info.get('gender','m')) # 可设置默认值,找到了则返回正确值,没找到则返回默认值

字典的删:

info = {'name': '吴彦祖','age':18}
print('删除前:%s'%info['name'])
del info['name'] # 删除整个键值对
print('删除后:%s'%info['name'])

print('删除前:%s'%info)
del info # 删除整个字典变量
print('删除后:%s'%info)

# [clear]
print('清空前:%s'%info)
info.clear() # 清空得到空的字典
print('清空后:%s'%info)

补充:通过枚举函数enumerate()为对象添加下标

mylist = ['a','b','c','d']
for i,x in enumerate(mylist): # enumerate让我们能同时获取下标和内容
    print(i,x)

3.5集合

3.6总结:

在这里插入图片描述

4.函数

补充:在函数内写global a是申明当前使用的是全局变量

5.文件操作

常用访问模式:
r:以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。
w:打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
rb:以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认格式。
wb:以二进制格式打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
文件读取的三种方式

# read方法,读取指定的字符,开始时定位在文件头部,每执行一次向后移动指定的字符数
f = open('test.txt','r')
content = f.read(5) # 读5个字符出来
print(content)
content = f.read(5)
print(content)
f.close()

read(n):读第n个字符
readlines():把整个文档都读入了一个list中,每行对应list的一个元素
readline():一次读一行

5.3文件的相关操作

有时候需要对文件进行重命名、删除等曹走,python中的os有这些功能

5.3.1文件重命名

import os
os.rename(需要修改的文件名, 新的文件名)

5.3.2删除文件

import os
os.remove(待删除的文件)

5.3.3创建文件夹

import os
os.mkdir(文件夹名)

5.3.4获取当前目录

import os
os.getcwd()

5.3.5改变默认目录

import os
os.chdir('../')

5.3.6获取目录列表

import os
os.listdir('./')

5.3.7删除文件夹

import os
os.rmdir(文件夹名)

6.错误与异常

捕获异常:

# 捕获异常
try:
    print('--------test------1---')
    f = open('123.txt','r')
    print('--------test------2---')
except IOError: # 文件没找到属于IO异常-IOError(输入输出异常)
    pass        # 捕获异常后,执行的代码

异常类型想要被捕获,必须要一致

try:
    print(num)
except NameError:  # 异常类型想要被捕获,必须要一致
    print('产生错误了')

将可能产生的所有异常类型,都放到括号中

try:
    print('--------test------1---')
    f = open('test1.txt','r')
    print('--------test------2---')
    print(num)
except (NameError, IOError):  # 将可能产生的所有异常类型,都放到括号中
    print('产生错误了')

获取错误描述(except Error类型 as result)

try:
    print('--------test------1---')
    f = open('123.txt','r')
    print('--------test------2---')
    print(num)
except (NameError, IOError) as result:  
    print('产生错误了')
    print(result)

捕获所有异常(except Exception)

try:
    print('--------test------1---')
    f = open('123.txt','r')
    print('--------test------2---')
    print(num)
except Exception as result: # Exception可以承接任何异常
    print('产生错误了')
    print(result)

try…finally 和嵌套

import time
try:
    f = open('test1.txt','r') # 文件打开

    try :
        while True:
            content = f.readline()
            if len(content) == 0:
                break
            time.sleep(2)
            print(content)
    finally:            # 如果能进入第二层try,说明文件打开了,文件打开。第二层try无论报任何错,都需要colse文件
        f.close()
        print('文件关闭')

except Exception as result:
    print('发生异常')
    print(result)

课堂作业4

# 通过python新建一个gushi.txt,选择一首古诗写入文件中
def write_file(file_name,gushi):
    f = open(file_name,'w') # 在这里我默认写文件不会出错
    try:
        f.writelines(gushi)
    except Exception as result:
        print('文件写入出错了,原因是:',result)
    finally:
        f.close()


write_file('gushi.txt',['床前明月光\n','疑是地上霜\n','举头望明月\n','低头思故乡'])


# 另外写一个函数,读取指定文件gushi.txt,将内容复制到copy.txt中,并在控制台输出'复制完毕'。
def read_file(file_name):
    try:
        f = open(file_name,'r')
        try:
            content = f.readlines()
        finally:
            f.close()
        return content
    except Exception as result:
        print('读取文件错误,原因是:',result)


content = read_file('gushi.txt')
write_file('copy.txt', content)
print('复制完毕')

Python爬虫(任务驱动)

1.任务介绍

需求分析
爬取豆瓣电影Top250的基本信息,包括电影的名称、豆瓣评分、评价数、电影概况、电影链接等。要爬取的网站

2.爬虫初识

什么是爬虫:网络爬虫,是一种按照一定规则自动抓取互联网信息的程序或者脚本。由于互联网数据的多样性和资源的有限性,根据用户需求定向抓取相关网页并分析已成为如今主流的爬取策略。
爬虫的本质是什么模拟浏览器打开网页,获取网页中我们想要的那部分数据。

3.基本流程

准备工作:通过浏览器查看分析目标网页,学习编程基础规范。
获取数据:通过HTTP库向目标站点发起请求,请求可以包含额外的header等信息,如果服务器能正常响应,会得到一个Response,便是所要获取的页面内容。
解析内容:得到的内容可能是HTML、json等格式,可以用页面解析库正则表达式等进行解析。
保存数据:保存形式多样,可以存为文本,也可以保存到数据库,或者保存特定格式的文件。

3.1准备工作

URL分析:
第一页对应的URL为:https://movie.douban.com/top250
第二页对应的URL为:https://movie.douban.com/top250?start=25&filter=
其中的&filter=没有取值,所以可以去掉并不会影响,有用的主要是start=25,他表示显示下标从25开始的电影(下标从0开始,所以实际上对应的标签是26)

总结:
页面包括250条电影数据,分10页,每页25条
每页的URL的不同之处:最后的数值=(页数-1)*25

3.1.1 分析页面

借助浏览器开发者工具来分析网页,在Elements下找到需要的数据位置

3.1.2 编码规范

一般Python程序第一行需要加入# -*- coding: utf-8 -*- 或者 # coding=utf-8,这样可以在代码中包含中文
Pytho文件中可以加入main函数用于测试程序:if __name__ == '__main__':

3.1.3 引入模块

模块(module):用来从逻辑上组织python代码(变量、函数、类),本质就是py文件,提高代码的可维护性。Python使用import来导入模块,如import sys
模块module一般情况下是以.py来后缀的文件。

3.2获取数据

补充urllib

get请求:

import urllib.request

#获取一个get请求
response = urllib.request.urlopen('http://www.baidu.com') # 等同于request.get(),后者更简单
print(response.read().decode('utf-8'))  # 一行显示的话需要打开soft-wrap,参考:https://blog.csdn.net/qq_38157825/article/details/89331743

当用urllib去获取一个网页的时候,建议对返回来的内容进行一个解码
补充:一个提供http请求和响应服务的测试网站:http://httpbin.org/
post请求:

#获取一个post请求
import urllib.parse
data  = bytes(urllib.parse.urlencode({'hello':'world'}),encoding='utf-8')
response = urllib.request.urlopen('http://httpbin.org/post',data)
print(response.read().decode('utf-8'))

超时处理:

# 超时处理,try_except
try:
    response = urllib.request.urlopen('http://httpbin.org/get',timeout=0.01)
    print(response.read().decode('utf-8'))
except urllib.error.URLError as e:
    print('time out!')

获取头部信息:

response = urllib.request.urlopen('http://www.baidu.com')
# print(response.status) # response包含状态码
print(response.getheaders()) # response包含头部信息
print(response.getheader('Server')) # 拿具体一个属性就是getheader

如果被发现是爬虫,可能会返回418Error(有一个梗-我是茶壶),所以需要伪装成浏览器
模拟浏览器:

url  = 'http://httpbin.org/post'
headers = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36 QQBrowser/4.4.119.400'
}  # 键值对可以增加更多
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'))

访问豆瓣:

url = 'https://www.douban.com'
headers = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36 QQBrowser/4.4.119.400'
} # 伪装浏览器
req = urllib.request.Request(url = url,headers=headers)
response = urllib.request.urlopen(req)
print(response.read().decode('utf-8'))

补充:有时User-Agent只指定为Mozilla/5.0就可以伪装成功了
补充contrl+shift+J可以快速合并到一行

补充BeautifulSoup

补充资料:
beautifulsoup4简介
Beautiful Soup 4.2.0 文档

BeautifulSoup4将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种:

  • Tag
  • NavigableString
  • BeautifulSoup
  • Comment
from bs4 import BeautifulSoup

file = open('./baidu.html','rb')
html = file.read()
bs = BeautifulSoup(html,'html.parser')

#1.Tag: 标签
print(bs.title) # 返回:<title>百度一下,你就知道 </title>
# print(bs.a)
# print(bs.head)
print(type(bs.head)) # 类型为bs4.element.Tag

#2.NavigableString: 标签里的内容(字符串)
print(bs.title.string) # 返回:百度一下,你就知道
print(type(bs.title.string)) # 类型为bs4.element.NavigableString
print(bs.a.attrs) # 拿到一个标签里面的所有属性

#3.BeautifulSoup:  表示整个文档
print(type(bs))
print(bs.name)
print(bs.attrs)
print(bs)

#4.comment: 是一个特殊的NavigableString,但输出的内容不包含注释符号
print(bs.a.string)
print(type(bs.a.string))

文档的遍历:

# 文档的遍历
print(bs.head.contents) # 返回一个list
print(bs.head.contents[1])
# 更多内容,搜索网络相关

文档的搜索:

# (1)find_all()--最常用
# 字符串过滤:会查找与字符串完全匹配的内容
t_list = bs.find_all('a') # 查询所有的a标签
# print(t_list)

import re
# 正则表达式:使用search()方法来匹配内容
t_list = bs.find_all(re.compile('a')) # 查询其中带有字符a的所有标签
# print(t_list)

# 方法:传入一个函数(方法),根据函数的要求来搜索(了解)
def name_is_exists(tag): #搜索并返回标签里面含有name属性的
    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) # class是python的保留字,所以bs4作者加了下划线(规定)
t_list = bs.find_all(href='http://news.baidu.com') # 精确查找
# for item in t_list:
    # print(item)

# 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')) # 应用正则表达式来查找包含特定文本的内容(标签里的字符串)
# for item in t_list:
    # print(item)

# 4.limit参数
t_list  = bs.find_all('a',limit=3) # 限制获取的个数
# for item in t_list:
    # print(item)


# 5.css选择器
t_list = bs.select('title') # 通过标签来查找
t_list = bs.select('.mnav') # 通过类名来查找,.表示查找的是类,跟的mnav是类名
t_list = bs.select('#u1')   # 通过id来查找,#表示查找的是id,跟的u1是id名
t_list = bs.select("a[class='bri']") # 通过属性来查找
t_list = bs.select('head>title') # 通过子标签来查找
# for item in t_list:
#     print(item)


t_list = bs.select('.mnav ~ .bri') # 跟.mnav是兄弟的一个标签,且该兄弟标签值是.bri
print(t_list[0].get_text())

补充:re模块

正则表达式的常用操作符号(1)
在这里插入图片描述在这里插入图片描述
补充史上最全正则表达式大全链接
补充: Re库主要功能函数
在这里插入图片描述
正则表达式的匹配模式控制在这里插入图片描述

3.3解析内容

解析页面内容:
1.使用BeautifulSoup定位特定的标签位置
2.使用正则表达式找到具体的内容

对爬取的html进行解析
1.使用BeautifulSoup定位特定的标签位置(类名为’item’的div标签):

soup = BeautifulSoup(html, 'html.parser')    #使用html.parser解析器
for item in soup.find_all('div', class_="item"): # 查找符合要求的字符串,形成列表

注意: class是python关键字,所以必须写class_

2.使用正则表达式找到具体的内容:(以解析’影片详情链接’举例)
定义查找’影片详情’的正则表达式:

# 影片详情链接的规则
findLink = re.compile(r'<a href="(.*?)">')

利用re库来通过正则表达式查找指定的字符串:

link = re.findall(findLink, item)[0]     #re库用来通过正则表达式查找指定的字符串

补充python 爬虫爬取内容时, \xa0 、 \u3000 的含义与处理方法

str = str.replace(u'\xa0', u'') #去掉'\xa0'

3.4保存数据

3.4.1Excel表存储

利用python库xlwt将抽取的数据datalist写入Excel表格

# 保存数据
import xlwt # 进行excel操作
def saveData(datalist, savepath):
    print('save....')
    book = xlwt.Workbook(encoding='utf-8') # 创建工作簿
    sheet = book.add_sheet('豆瓣电影Top250')# 添加工作表
    col = ('电影详情链接','图片链接','影片中文名','影片外文名','评分','评价数','概况','相关信息')							   # 列名设置
    for i in range(0,8):
        sheet.write(0,i,col[i]) 		   # 写入列名
    for i in range(0,250):
        print("第%d条"%(i+1))
        data = datalist[i]
        for j in range(0,8):
            sheet.write(i+1,j,data[j])    # 往工作表单元格写入数据
    book.save(savepath)  			  	  # 保存表格

补充: SQLite

连接数据库:

import sqlite3
# 1.连接数据库
conn = sqlite3.connect('test.db')  # 打开或创建数据库文件
print('成功打开数据库')

创建数据表:

# 2.创建数据表
conn = sqlite3.connect('test.db')  # 打开或创建数据库文件
print('成功打开数据库')
c = conn.cursor() # 获取游标

sql = '''
    create table company
        (id int primary key not null,
        name text not null,
        age int not null,
        address char(50),
        salary real);
'''

c.execute(sql)    # 执行sql语句
conn.commit()        # 提交数据库操作
conn.close()      # 关闭数据库连接
print('成功建表')

插入数据:

conn = sqlite3.connect('test.db')  # 打开或创建数据库文件
print('成功打开数据库')
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 (2,'李四',30,'重庆',15000);
'''

c.execute(sql1)    # 执行sql语句
c.execute(sql2)    # 执行sql语句
conn.commit()        # 提交数据库操作
conn.close()      # 关闭数据库连接
print('插入数据完毕')

查询数据:

conn = sqlite3.connect('test.db')  # 打开或创建数据库文件
print('成功打开数据库')
c = conn.cursor() # 获取游标

sql = '''
    select id, name, address, salary from company


'''

cusor = c.execute(sql)    # 执行sql语句

for row in cusor:
    print('id = ', row[0])
    print('name = ', row[1])
    print('address = ', row[2])
    print('salary = ', row[3],'\n')


# conn.commit()           # 查询不需要提交
conn.close()      # 关闭数据库连接
print('查询完毕')

最终爬虫代码

#-*- coding = utf-8 -*-
#@Time ; 15/06/2020 10:48
#@Author : 张亿锋
#@File : spider.py
#@Software : PyCharm


from bs4 import BeautifulSoup  # 网页解析,获取数据
import re   # 正则表达式,进行文字匹配
import urllib.request, urllib.error # 制定URL,获取网页数据
import xlwt # 进行excel操作
import sqlite3 # 进行SQLite数据库操作


def main():
    baseurl = 'https://movie.douban.com/top250?start='
    #1.爬取网页
    datalist = getData(baseurl)

    #2.保存数据到excel表格中
    # savepath = '豆瓣电影Top250.xls'
    # saveData(datalist,savepath)

    #2.保存数据到数据库中
    dbpath = 'movie.db'
    saveData2DB(datalist, dbpath)


#影片详情链接的规则
findLink = re.compile(r'<a href="(.*?)">')     #创建正则表达式对象,表示规则(字符串的模式),这里?是最小匹配,即非贪婪匹配,
                                               #括号表示一个组,等会findall返回的就是这个组的匹配上的字符串的这个组的内容
# findmagSrc = re.compile(r'<img.*src="(.*?)".*>')
#影片图片的链接
findmagSrc = re.compile(r'<img.*src="(.*?)"',re.S) #re.S模式:使.匹配包括换行在内的所有字符
#影片片名
findTitle = re.compile(r'<span class="title">(.*)</span>')
#影片评分
findRating = re.compile(r'<span class="rating_num" property="v:average">(.*)</span>')
#找到评价人数
findJudge = re.compile('<span>(\d*)人评价</span>')
#找到概况
findInq = re.compile(r'<span class="inq">(.*)</span>')
#找到影片的相关内容
findBd = re.compile(r'<p class="">(.*?)</p>',re.S) # 必须要有?, 否则匹配会去匹配直到第二个</p>


# 爬取网页
def getData(baseurl):
    datalist = []
    for i in range(0,10): # 调用获取页面信息的函数10次
        url = baseurl + str(i*25)
        html = askURL(url) # 保存获取到的网页源码


        # 2.逐一解析数据
        soup = BeautifulSoup(html, 'html.parser')    #使用html.parser解析器
        for item in soup.find_all('div', class_="item"): # 查找符合要求的字符串,形成列表
            # print(item)   # 测试: 查看电影item全部信息
            data = []       # 保存一部电影的所有信息
            item = str(item)

            #影片详情的链接
            link = re.findall(findLink, item)[0]     #re库用来通过正则表达式查找指定的字符串
            data.append(link)                        #添加链接

            imgSrc = re.findall(findmagSrc, item)[0]
            data.append(imgSrc)                      #添加图片

            titles = re.findall(findTitle, item)     #片名可能只有一个中文名,没有外文名
            if (len(titles) == 2):
                ctitle = titles[0]
                data.append(ctitle)                  #添加中文名
                otitle = titles[1].replace('/','')   #去掉无关的符号
                otitle = otitle.replace(u'\xa0', u'')  # 去掉无关的符号
                data.append(otitle)                  #添加外文名
            else:
                data.append(titles[0])
                data.append(' ')                     #留空

            rating = re.findall(findRating,item)[0]
            data.append(rating)                      #添加评分

            judgeNum = re.findall(findJudge,item)[0]
            data.append(judgeNum)                    #添加评价人数

            inq = re.findall(findInq,item)
            if len(inq) != 0:
                inq = inq[0].replace('。','')        #去掉句号
                data.append(inq)  # 添加概述
            else:
                data.append('')

            bd = re.findall(findBd, item)[0]
            # \s是指空白,包括空bai格、换行、tab缩进等所有的空白,单独的问号表示匹配前面的子表达式0次或者1次
            bd = re.sub('<br(\s+)?/>(\s+)?',' ',bd)  #去掉<br/>
            bd = re.sub('/',' ',bd)                  #替换/
            bd = bd.replace(u'\xa0', u'')
            data.append(bd.strip())                  #去掉前后的空格

            datalist.append(data)                    #把处理好的一部电影信息放入datalist

    # print(datalist) # 测试
    return datalist


# 得到指定一个URL的网页内容
def askURL(url):
    head = {} # 模拟浏览器头部信息,向豆瓣服务器发送消息
    # 用户代理,表示告诉豆瓣服务器,我们是什么类型的机器,浏览器(本质上是告诉刘篮球,我们可以接受什么水平的文件内容)
    head['User-Agent'] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36 QQBrowser/4.4.119.400' # contrl+shift+J可以快速合并到一行
    request = urllib.request.Request(url,headers=head)
    html = ''
    try:
        response = urllib.request.urlopen(request)
        html = response.read().decode('utf-8')
        # print(html)
    except urllib.error.URLError as e:
        if hasattr(e,'code'):
            print(e.code) # e.code是错误代码
        if hasattr(e,'reason'):
            print(e.reason) # e.reason是错误原因
    return html


# 保存数据
def saveData(datalist, savepath):
    print('save....')
    book = xlwt.Workbook(encoding='utf-8')
    sheet = book.add_sheet('豆瓣电影Top250')
    col = ('电影详情链接','图片链接','影片中文名','影片外文名','评分','评价数','概况','相关信息')
    for i in range(0,8):
        sheet.write(0,i,col[i]) # 列名
    for i in range(0,250):
        print("第%d条"%(i+1))
        data = datalist[i]
        for j in range(0,8):
            sheet.write(i+1,j,data[j])

    book.save(savepath)


def saveData2DB(datalist, dbpath):
    init_db(dbpath)
    conn = sqlite3.connect(dbpath)
    cur = conn.cursor()

    for data in datalist:
        for index in range(len(data)):
            if index == 4 or index == 5:
                continue
            data[index] = '"'+str(data[index])+'"' # score和rated不需要

        sql = '''
            insert into movie250 (
            info_link, pic_link, cname, ename, score, rated, introduction, info) values (%s)
            '''%",".join(data)
        print(sql)
        cur.execute(sql)
        conn.commit()
    cur.close()
    conn.close()




def init_db(dbpath):
    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
        )    
    '''     # 创建数据表
    conn = sqlite3.connect(dbpath)
    cursor = conn.cursor()
    cursor.execute(sql)
    conn.commit()
    conn.close()


if __name__ == '__main__':
    main()
    # init_db('movietest.db') # 测试
    print('爬取完毕')

数据可视化

1.Flask入门

1.1.1了解框架

Flask作为Web框架,它的作用主要是为了开发Web应用程序。那么我们首先来了解下Web应用程序。Web应用程序(World Wide Web)诞生最初的目的,是为了利用互联网交流工作文档。

1.2.1从Helloworld开始

框架:

from flask import Flask, render_template,request
app = Flask(__name__) # 初始化
# 路由解析,通过用户访问的路径,匹配相应的函数
@app.route('/')
def hello_world():
    return 'Hello World!'
if __name__ == '__main__':
    app.run()

补充: debug模式务必开启,开启方法是:右上角
在这里插入图片描述
点击Edit Configurations后,勾选FLASK_DEBUG
通过访问路径,获取用户的字符串参数:

# 通过访问路径,获取用户的字符串参数
@app.route('/index/<name>')
def welcome(name):
    return '你好,%s'%name

通过访问路径,获取用户的整型参数:

# 通过访问路径,获取用户的整型参数
@app.route('/index/<int:id>')
def welcome2(id):
    return '你好,%d号的会员'%id

补充: 此外,还可设置float类型。总之,通过填写不同的参数(不同的访问路径),可以进入不同的路径,到不同的网页。
注意: 路由路径不能重复,用户只能通过唯一路径来访问特定的函数。(当然可以通过不同的函数进入之后再跳转到其他函数,但这里不做介绍)
返回利用render_template(jinjia2)渲染网页:

# 返回给用户渲染后的网页文件
@app.route('/i')
def index2():
    return render_template('index.html')

动态-向网页传递一个变量(利用render_template(jinjia2)进行渲染):

三种变量类型:
普通变量
列表类型变量
字典类型变量

1.传递普通变量:

@app.route('/i3')
def index3():
    time = datetime.date.today()    # 普通变量
    return render_template('index.html',var = time)

在网页html中使用var变量的方式:

今天是{{ var }}, 欢迎光临.

2.传递列表类型变量:

@app.route('/i3')
def index3():
    time = datetime.date.today()    # 普通变量
    name = ['小张','小王','小赵']     # 列表类型
    return render_template('index.html',var = time, list = name)

在网页html中使用list变量的方式:

{% for data in list %}   <!--用大括号和百分号括起来的是控制结构,还有if,可自行了解-->
	<li> {{ data }} </li>
{% endfor %}

3.传递字典类型变量:

@app.route('/i3')
def index3():
    time = datetime.date.today()    # 普通变量
    name = ['小张','小王','小赵']     # 列表类型
    task = {'任务':'打扫卫生','时间':'3小时'} # 字典类型
    return render_template('index.html',var = time, list = name, task = task)

注意: 实际中常采用类似task=task的写法,前面的表示传入html的参数名,后面是在.py文件中的变量名
在网页html中使用task变量的方式:(这里结合了表格)

<table border="1">
	{% for key,value in task.items() %} 
		<tr>
			<td>{{ key }}</td>
            <td>{{ value }}</td>
       	</tr>
	{% endfor %}
</table>

表单提交

1.构建表单提交的页面register.html:

{#<form action="http://localhost:5000/result" method="post">#}
<form action="{{ url_for('result')}}" method="post"> <!--不使用localhost,而是通过反向路由(根据路由函数名得到路由地址)-->
    <p>姓名: <input type="text" name="姓名"></p>
    <p>年龄: <input type="text" name="年龄"></p>
    <p>性别: <input type="text" name="性别"></p>
    <p>地址: <input type="text" name="地址"></p>
    <p><input type="submit" value="提交"></p>
</form>

注意: 不使用localhost,而是通过反向路由(根据路由函数名得到路由地址)
2.在app.py中添加register.html的访问路径和访问路由:

# 表单提交
@app.route('/test/register')
def register():
    return render_template('test/register.html')

3.构建接受表单提交显示的页面result.html:

<table border="1">
	{% for key,value in result.items() %}
	    <tr>
	        <th>{{ key }}</th>
	        <td>{{ value }}</td>
	    </tr>
	{% endfor %}
</table>

4,构建接受表单的访问路径和访问路由:

from flask import request
@app.route('/result', methods = ['POST','GET'])
def result():
    if request.method == 'POST':
        result = request.form # request可以获取所有提交的表单信息,生成字典
        return render_template('test/result.html',result = result)

注意: 接受表单提交的路由,需要指定methodsPOST

豆瓣数据可视化

3.1首页制作

3.2列表页制作

2.Echarts应用

补充: GitHub上的js和css一般是不能单独下载的。如需下载需要下载整个文件,比如下载.zip后缀的包,然后解压,取出自己想要的js或css文件。
注意: 字符串经过jinjia渲染后可能会出现转义字符的问题

for item in data:
    score.append(str(item[0])) # score是字符串列表
    num.append(item[1])
cur.close()
conn.close()
return render_template('score.html', score=score, num=num)

html中按通常方式调用score

data: {{ score }}

html源码中出现转义字符显示问题:

data: [&#39;8.3&#39;, &#39;8.4&#39;, &#39;8.5&#39;, &#39;8.6&#39;, &#39;8.7&#39;, &#39;8.8&#39;, &#39;8.9&#39;, &#39;9&#39;, &#39;9.1&#39;, &#39;9.2&#39;, &#39;9.3&#39;, &#39;9.4&#39;, &#39;9.5&#39;, &#39;9.6&#39;, &#39;9.7&#39;]

修改的方法:在html中用下面代码调用score(把内容转化成json格式的)

data: {{ score|tojson }}

3.WordCloud应用

注意:pycharmsettings-project interpreter中找不到jieba模块的话,可以考虑点击
在这里插入图片描述

51job数据可视化项目

1.项目说明

1.1需求说明

项目名称: 51job招聘网数据爬取分析平台
项目背景: 随着互联网在中国的普及,网络招聘景气不断攀升。它允许以更加灵活的交互方式,提供更加丰富 的信息资源。为求职者提供更加多样、精确、新鲜的招聘信息。

项目意义: 面对眼花缭乱的招聘信息,帮助求职者能够通过招聘信息的数据可视化,分析招聘薪资等信息的分 布,明确自己的方向,找到适合的工作。

项目目标:
1、实现通过搜索对相关招聘的主要信息进行展示、下载(选做)。
2、对搜索出的信息进行地域、薪资、工作经验、学历、职责与要求等个方面的数据可视化分析。

1.2 技术要求

技术环境: Python3、urllib库、flask框架、Echarts.js、wordcloud库、sqlite3数据库

分工要求: 每组4-5人,每人有一个菜单项,完成相应的数据可视化的功能展示,功能不能相同。
菜单项包括:首页、列表页(分页选做)、Echarts图表2-3个、词云0-1个、团队介绍页(选做)

考核内容:
+ 基本功能:
1.使用爬虫爬取51job数据至少1000条,并保存到数据库中
2.应用flask框架完成网站搭建并能够本地访问
3.能够引导用户搜索关键词,完成相应内容可视化的展现

+ 鼓励创新:
1.数据呈现的多样化:多种图表形式
2.数据维度的设计:能够从不同维度的数据分析,为用户提供更多价值
3.界面表现的美化

提交材料:
项目源码
实验报告

2.制作流程

2.1 爬取数据

爬取列表

  • 51job字符集为gbk
    所以编码解析需要设置为gbk
  • url二次解析
    在51job上搜索中文(如大数据),在url显示的其实是二次编码后的结果,为此我们需要模拟二次编码,代码如下
from urllib import parse
keyword = parse.quote('大数据')
newKW = parse.quote(keyword)  # 二次编码
  • 使用选择器实现文字提取
    如果不想使用正则表达式从html中提取数据,则可以通过选择器提取数据,具体的,
    先通过F12定位到需要html中需要提取的区域,然后根据标签递进顺序进行选择,代码如下:
html = open('jobList.heml', 'r')
bs = BeautifulSoup(html, 'html.parser')
eldiv = bs.select('.el > .t1 > span > a')
for link in eldiv:
	print(link.text.strip())

爬取详情

  • 字符串截取:str.[n:m] 截取从下标n到m个字符
  • 分隔:str.split('|') 按照|分隔字符串为列表
  • 去除特殊符号:str.replace('/r','') 替换'/r'为空白字符串
  • 去除前后空格:str.strip()

2.2 数据保存

建议采用成熟的模版
前端页面
模版推荐:
https://bootstrapmade.com/ 国外网站,需要翻墙才能访问
https://colorlib.com/wp/templates/ 国内可访问
https://colorlib.com/wp/free-bootstrap-admin-dashboard-templates/ 加载较慢

视频里面的模版:
https://startbootstrap.com/themes/sb-admin-2/

列表显示
表单制作:

  • 表单传值

2.3 搭建框架

2.4 制作图表

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章