python 爬虫

1. 手写第一个 python 爬虫

# 爬虫: 用程序来获取网站上的资源
# 常用 encoding='utf-8'  encoding='gbk'

# 1. 导入 urllib.request  urlopen
from urllib.request import urlopen

# 2. 设置目标网址,并使用urlopen来访问
# 访问完成是有返回数据的 使用resp来接收
url = 'http://www.baidu.com'
resp = urlopen(url)

# encoding('utf-8') 将字节转换为字符串
# 使用 utf-8 编码格式 还有 gbk 格式
# as 起别名
with open('mybaidu.html', mode='w', encoding='utf-8') as f:
    f.write(resp.read().decode('utf-8'))

print('over')

resp.close() # 关闭 resp  不然多了会出现奇奇怪怪的问题

2. web请求过程剖析



3. HTTP协议

4. requests 第三方库 清华源

# 先下载 requests 第三方包
# pip install requests

# 国内源  清华源
# 临时使用一次
# pip install -i https://pypi.tuna.tsinghua.edu.cn/simple requests

# 导入 requests 包
import requests
query = input('输入一个你要搜索的明星:')

url = f'https://www.sogou.com/web?query={query}'
# 在搜索框里面的 url 的请求方式都是 get 方式
# 请求完成都会有返回的数据

# 处理了一个小小的反爬问题
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36"
}

resp = requests.get(url, headers=headers)

print(resp)
print(resp.text) # 获取页面源代码

resp.close() # 关闭 resp  不然多了会出现奇奇怪怪的问题

5. pip 切换为清华源(国内镜像源)

# 临时使用一次  将 some-package 切换为包的名字
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple some-package
# 设为默认
python -m pip install --upgrade pip
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple

# 如果您到 pip 默认源的网络连接较差,临时使用本镜像站来升级 pip:
python -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple --upgrade pip

6. 两个爬虫小文件

传参数使用的变量名
get    params
post   data


# request 入门
import requests

url = 'https://fanyi.baidu.com/sug'

s = input('请输入你要翻译的单词!')

dat = {
    'kw': s
}

# 发送数据和参数,会有返回值用 resp 来接收
# 发送 post 请求, 发送的数据必须在字典中,使用data来传递数据
resp = requests.post(url, data=dat)

# 1. 这里不能使用 text 因为会出现乱码的情况
# 2. 直接将数据转换为 JSON 文件 => dict(字典)
print(resp.json())

resp.close() # 关闭 resp  不然多了会出现奇奇怪怪的问题
# request豆瓣
import requests

url = "https://movie.douban.com/j/chart/top_list"

# 优化参数  参数里面有空格(恶心的坑)会获取到一个空列表 []
param = {
    "type": "24",
    "interval_id": "100:90",
    "action": "",
    "start": 0,
    "limit": 20,
}

# 防止反爬  设置请求头 User-Agent
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36"
}

# url 和 params 名字一样可以省略(只写一个即可)
# 注意: get 请求 参数是 params    而 post 请求参数是 data
resp = requests.get(url=url, params=param, headers=headers)

# 被反爬了 需要设置 User-Agent
# print(resp.request.headers)

# 查看爬取到的数据
print(resp.json())

resp.close() # 关闭 resp  不然多了会出现奇奇怪怪的问题

第二章 数据解析概述

1.  re解析
2.  bs4解析
3.  xpath解析

7. re解析 使用 正则表达式

元字符: 具有固定含义的特殊符号 常⽤元字符

. 匹配除换⾏符以外的任意字符
\w 匹配字⺟或数字或下划线
\s 匹配任意的空⽩符
\d 匹配数字

^ 匹配字符串的开始
$ 匹配字符串的结尾

[a-zA-Z0-9] 匹配字符组中的字

量词: 控制前⾯的元字符出现的次数

* 重复零次或更多次
+ 重复⼀次或更多次
? 重复零次或⼀次
{n} 重复n次
 {n,} 重复n次或更多次
{n,m} 重复n到m次

贪婪匹配和惰性匹配 (很重要)

 .* 贪婪匹配   竟可能多的去匹配
.*? 惰性匹配   竟可能少的去匹配

8. re模块

# 导入 re 模块, 常用的 finditer方法 返回的是迭代器
import re

# findall 返回符合正则表达式的所有内容
list = re.findall(r'\d+', "我的手机号:1008611, 还有一个是10000")
print(list)

# finditer 常用
# finditer 返回符合正则表达式的所有内容 (返回的是迭代器)
# 要拿到内容 需要使用 .group函数来获取返回的内容
iter = re.finditer(r'\d+', "我的手机号:1008611, 还有一个是10000")
print(iter)

# 只有 finditer 才有 group 方法
for x in iter:
    print(x.group())

# search返回的是Match数据,那数据需要使用.group()
# 只获取第一个数据,获取到后立马返回(不会再找下一个)
s = re.search(r'\d+', "我的手机号:1008611, 还有一个是10000")
print(s.group())  # 1008611

# match 是从头开始匹配,若开头与正则表达式不匹配,则会报错
z = re.match(r'\d+', "1008611, 还有一个是10000")
print(z.group())

# 预加载正则表达式  compile 表示预加载 (可以重复使用)
obj = re.compile(r'\d+')

it = obj.finditer("我的手机号:1008611, 还有一个是10000")
print(it)

for y in it:
    print(y.group())

result = obj.findall('呵呵, 你要还我一个100000000')
print(result)

9. 在 html 中提取所需要的数据

s = """
<div class='jar'><span id='1'>中国联通</span></div>
<div class='jj'><span id='2'>中国电信</span></div>
<div class='aa'><span id='3'>中国移动</span></div>
"""

obj1 = re.compile(r"<div class='(?P<id>.*?)'><span id='.*?'>(?P<xinming>.*?)</span></div>", re.S) # re.S 匹配到换行符

result1 = obj1.finditer(s)

for xx in result1:
    print(xx.group('id'))
    print(xx.group('xinming'))

#  (?P<xinming>.*?)   (?P<里面写名字>)  后续通过属性名字来获取我们所需要的数据

10. 获取豆瓣top250前面的几条数据

# 1.  获取页面源代码 requests
# 2.  提取页面的有效信息  re模块
import requests
import re
# 创造 csv 文件 方便分析数据
import csv

url = 'https://movie.douban.com/top250'

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36"
}
resp = requests.get(url, headers=headers)

# 获取到页面的内容
page_content = resp.text

# 解析数据 (到豆瓣top250源码 分析得来)   re.S 匹配到换行符
obj = re.compile(r'<li>.*?<div class="item">.*?<span class="title">(?P<name>.*?)'
                 r'</span>.*?<p class="">.*?<br>(?P<year>.*?)&nbsp.*?<span '
                 r'class="rating_num" property="v:average">(?P<score>.*?)</span>.*?'
                 r'<span>(?P<num>.*?)</span>', re.S)

# 开始匹配
result = obj.finditer(page_content)

# 创建data.csv文件
f = open('data.csv', mode='w')
# 写入文件
csvwriter = csv.writer(f)

# strip 去除数据前面的空白
for it in result:
    # print(it.group('name'))
    # print(it.group('year').strip())
    # print(it.group('score'))
    # print(it.group('num'))

    # 将数据写入带字典中  groupdict() 返回字典
    dic = it.groupdict()
    # 处理年数据的空白符  strip() 去除头尾的空格
    dic['year'] = dic['year'].strip()
    # 将 数据写入到dat.csv 文件中
    # 注意: writerow()将一个列表全部写入csv的同一行。
    csvwriter.writerow(dic.values())

resp.close()

f.close()
print('over!')
pycharm打开csv文件,csv文件中的中文出现乱码的解决方案

这是因为pycharm中默认设置是以utf-8的编码方式打开文件,而csv的文件正确读取方式是GBK,使用 UTF-8 自然会造成乱码

解决方法为:修改pycharm的默认设置为 GBK 即可。


11. pycharm 安装插件

1.   csv
2.   Translation

12. 获取 电影天堂 的部分下载链接

注意: SyntaxError:Non-UTF-8 code starting with '\xb6' in file E:/.../....py on line 16, but no encoding declared;

解决方法
# coding=utf-8

在第一行加上上面的代码(带 # 号)

# charset 表示使用什么字符集
charset = gbk 或者 utf-8
# coding=utf-8

# 1.  定位到2022年电影热片
# 2.  获取子页面的链接信息
# 3.  请求子页面获取我们需要的下载链接

import requests
import re

domain = "https://www.dytt89.com/"

# verify=False  去掉安全验证
resp = requests.get(domain, verify=False)

# 指定字符集
resp.encoding = 'gbk'

# print(resp.text)

# 设置re模块 预加载
# 获取到 ul 中的 li
obj1 = re.compile(r"2022必看热片.*?<ul>(?P<ul>.*?)</ul>", re.S)

# 获取子页面中href的内容
obj2 = re.compile(r"<a href='(?P<href>.*?)'", re.S)

# 定义第三个正则表达式
obj3 = re.compile(r'◎片  名 (?P<movie>.*?)<br />.*?<td '
                  r'style="WORD-WRAP: break-word" bgcolor="#fdfddf">'
                  r'<a href="(?P<download>.*?)">', re.S)

result1 = obj1.finditer(resp.text)

# 创建一个装 子页面链接的列表
child_href_list = []

for it in result1:
    ul = it.group('ul')

    # print(ul)
    result2 = obj2.finditer(ul)
    for itt in result2:
        # 拼接子页面链接  域名 + 子页面地址
        # 干掉子页面链接的第一个/(右斜杠)
        child_href = domain + itt.group('href').strip("/")
        # print(itt.group('href'))
        # 打印子页面的链接
        # print(child_href)

        # 将子页面的链接全部添加到列表中
        child_href_list.append(child_href)

# 3. 获取子页面的内容
for href in child_href_list:
    child_resp = requests.get(href)
    # 设置字符集
    child_resp.encoding = 'gbk'
    # 打印子页面的信息
    # print(child_resp.text)

    # 子页面中使用obj3的正则来匹配所需要的内容
    result3 = obj3.search(child_resp.text)
    # 打印从子页面提取的信息
    print(result3.group("movie"))
    print(result3.group("download"))

    # 终止循环(这里只获取第一条信息)
    # break

resp.close()
posted @ 2022-08-16 17:16  伴你如风  阅读(286)  评论(0编辑  收藏  举报