python爬虫

urllib

基本使用

# 使用 urllib 获取百度首页源码
# 导入包
import urllib.request

# 1、定义一个 url
url = 'http://www.baidu.com'

# 2、模拟浏览器向服务器发送请求获取数据(包含页面源码、状态码等信息)
response = urllib.request.urlopen(url)

# 3、获取响应中的页面的源码
# read() 返回的是二进制形式的数据
# decode() 将二进制数据转化为字符串
content = response.read().decode('UTF-8')

print(content)

一个类型六个方法

import urllib.request

url = 'http://www.baidu.com'

# 一个类型
# 类型 
response = urllib.request.urlopen(url)

# 六个方法
# 一个字节一个字节的读
# content = response.read()

# 返回五个字节
# content = response.read(5)

# 读取一行
# content = response.readline()

# content = response.readlines()
# print(content)

# 返回状态码
print(response.getcode())

# 返回 url 地址
print(response.geturl())

# 获取header 数据
print(response.getheaders())

下载

import urllib.request

# url_page = 'http://www.baidu.com'

# 第一个参数是下载的路径,第二个参数是文件的名字
# urllib.request.urlretrieve(url_page, 'baidu.html')

# 下载图片
# url_page = 'https://img2.baidu.com/it/u=3841260943,424697253&fm=253&fmt=auto&app=138&f=JPEG?w=1942&h=500'
# urllib.request.urlretrieve(url_page,'1.jpg')

# 下载音乐  
# url_page = 'https://webfs.ali.kugou.com/202301291027/33a3bd554d58e440c148a2359bdbad95/KGTX/CLTX001/72afa3abf78f1932d88adf991a298eb4.mp3'
# urllib.request.urlretrieve(url_page,'1.mp3')

# 下载视频 
url_page = 'https://mvwebfs.ali.kugou.com/202301291034/b20db341fcd0033b9ab66f879faf5b99/KGTX/CLTX002/85ab1659df2a13e40fdd490e130ff815.mp4'
urllib.request.urlretrieve(url_page,'1.mp4')

请求对象定制

import urllib.request

url_page = 'https://www.baidu.com'

# 这样直接请求,返回的数据会非常少,甚至没有数据,因为https 是加密的,我们遇到了反爬(UA反爬)
# response = urllib.request.urlopen(url_page)
# print(response)

headers= {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 Edg/109.0.1518.70'
}

# 请求对象的定制
# 因为 urlopen 方法中不能存储字典 所以 headers 不能直接放进去,需要封装一个对象
request = urllib.request.Request(url=url_page,headers=headers)
response = urllib.request.urlopen(request)
content = response.read().decode('utf-8')
print(content)

get 请求中的 quote 方法

quote 方法是将汉字变为 Unicode 编码

import urllib.request
import urllib.parse

url_page = 'https://www.baidu.com/s?wd='

# 请求参数定制,解决反爬
headers= {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 Edg/109.0.1518.70'
}
# 将搜索中的汉字变为 Unicode 编码
name = urllib.parse.quote('夜景')
request = urllib.request.Request(url=url_page+name, headers=headers)
response = urllib.request.urlopen(request)
content = response.read().decode('utf-8')
print(content)

get请求中的 urlencode 方法

应用场景 :路径中有多个参数时,需要用到该方法。将多个参数变为 Unicode 编码格式

import urllib.parse

data = {
    'wd': '夜景',
    'sex': '男'
}

param = urllib.parse.urlencode(data)
print(param) # wd=%E5%A4%9C%E6%99%AF&sex=%E7%94%B7

post 请求百度翻译

import urllib.request
import urllib.parse
import json

url_page = 'https://fanyi.baidu.com/sug'
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36'
}
data = {
    'kw': 'spider'
}
# post 请求的参数必须要进行编码
data = urllib.parse.urlencode(data).encode('utf-8')
# post 请求的参数不会拼接在地址的后面,而是需要放在请求对象定制的参数里
request = urllib.request.Request(url=url_page, data=data, headers=headers)
# 模拟浏览器发送请求
content = urllib.request.urlopen(request).read().decode('utf-8')
print(content)

# 将网页数据转换为 json 字符串
obj = json.loads(content)
print(obj)

# post 请求方式的参数必须编码
# 编码之后 必须调用encode 方法
# 参数是放在请求对象定制的方法中

post 请求百度翻译之详细翻译

import urllib.request
import urllib.parse
import json

url_page = 'https://fanyi.baidu.com/v2transapi?from=en&to=zh'
headers = {
    'Cookie': 'BIDUPSID=643E3D99B504A70B95F59B9A25CEA048; PSTM=1623503833; __yjs_duid=1_277416b758944186760da3faea8002a71626095611054; BAIDUID=C851CECEA9FC2A831B4853EBA67D2D52:FG=1; BAIDUID_BFESS=C851CECEA9FC2A831B4853EBA67D2D52:FG=1; BDUSS=lnR2RnMDd-bDZiUDNPNzlpc1g4dzJiV3dadHVVbk9zbVRNflp0YlJlRXpCNmhqRVFBQUFBJCQAAAAAAAAAAAEAAADucBdS1sq14zAzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADN6gGMzeoBjUm; BDUSS_BFESS=lnR2RnMDd-bDZiUDNPNzlpc1g4dzJiV3dadHVVbk9zbVRNflp0YlJlRXpCNmhqRVFBQUFBJCQAAAAAAAAAAAEAAADucBdS1sq14zAzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADN6gGMzeoBjUm; ZFY=0zcfPpXqrBhcJp1b5c4PVgzkmHWGkFplQJYEAetebIQ:C; BA_HECTOR=8haha081a50l0184al0l0kd21ht9gs21k; H_PS_PSSID=36556_37550_37139_37989_37938_38041_26350_22160_37881; Hm_lvt_64ecd82404c51e03dc91cb9e8c025574=1674968024; Hm_lpvt_64ecd82404c51e03dc91cb9e8c025574=1674968024; APPGUIDE_10_0_2=1; REALTIME_TRANS_SWITCH=1; FANYI_WORD_SWITCH=1; HISTORY_SWITCH=1; SOUND_SPD_SWITCH=1; SOUND_PREFER_SWITCH=1; ab_sr=1.0.1_YjRhNGE3ZWNlMDcxZDk3ZTY4YTMwY2I4ZjkyNmEwZWViZWYwZGUyNWI0NmNiMTUzOWI1NGViN2U2ZmI2ZTVmZjg4NDM4MjcwMmUyOGVjNTUwMzQxMDZiNzE1ZjkxYTBmYTlmODU4ZTJkMGI0Mjg4ZTA2MjkxZjQ5M2E5N2YwNzkyOTAwMWY3YmQ3MDhjNmM4YmM4MDIwZmU1YWI2MTg2ZWM1MDEyY2JmYTAyMTY0OWIyZWRhZTE0ZDQwMmI5OGQ3'
}
data = {
    'from': 'en',
    'to': 'zh',
    'query': 'spider',
    'transtype': 'realtime',
    'simple_means_flag': '3',
    'sign': '63766.268839',
    'tok': 'd5ad6e678d24b4a6fde6091f8b2b4bb2',
    'domain': 'common'
}
# post 请求的参数必须要进行编码
data = urllib.parse.urlencode(data).encode('utf-8')
# post 请求的参数不会拼接在地址的后面,而是需要放在请求对象定制的参数里
request = urllib.request.Request(url=url_page, data=data, headers=headers)
# 模拟浏览器发送请求
content = urllib.request.urlopen(request).read().decode('utf-8')
print(content)

# 将网页数据转换为 json 字符串
obj = json.loads(content)
print(obj)

ajax的get请求豆瓣电影第一页

import urllib.request

# 获取豆瓣电影第一页,并保存起来
url = 'https://movie.douban.com/j/chart/top_list?type=20&interval_id=100%3A90&action=&start=0&limit=20'

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36'
}

# 请求对象的定制
request = urllib.request.Request(url=url, headers=headers)
response = urllib.request.urlopen(request)
content = response.read().decode('utf-8')
print(content)

# 将数据下载到本地
# open 方法默认使用的是 gbk 的编码,如果我们要想保存汉字,那么需要在 open 方法中指定编码格式 utf-8
fp = open('douban.json', 'w', encoding='utf-8')
fp.write(content)
fp.close()

ajax的post请求肯德基官网

import urllib.request
import urllib.parse

def create_request(page):
    # 获取豆瓣电影第一页,并保存起来
    url = 'http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=cname'
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36'
    }
    data = {
        'cname': '北京',
        'pid': '',
        'pageIndex': page,
        'pageSize': '10'
    }
    data = urllib.parse.urlencode(data).encode('utf-8')
    # 请求对象的定制
    request = urllib.request.Request(url=url, data=data, headers=headers)
    return request

def get_content(request):
    response = urllib.request.urlopen(request)
    content = response.read().decode('utf-8')
    return content

def down_load(page, content):
    # 将数据下载到本地
    # open 方法默认使用的是 gbk 的编码,如果我们要想保存汉字,那么需要在 open 方法中指定编码格式 utf-8
    fp = open('kfc_'+str(page)+'.json', 'w', encoding='utf-8')
    fp.write(content)
    fp.close()

if __name__ == '__main__':
    start_page = int(input('请输入起始页码'))
    end_page = int(input('请输入结束页码'))
    for page in range(start_page,end_page+1):
        request = create_request(page)
        content = get_content(request)
        down_load(page, content)

Handler 基本使用

Handler 可以定制高级的请求头

随着业务逻辑的复杂 请求对象的定制已经满足不了我们的需求(动态cookie和代理不能使用请求对象的定制)

import urllib.request

url = 'http://www.baidu.com'
headers= {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 Edg/109.0.1518.70'
}
request = urllib.request.Request(url=url, headers=headers)

# 获取 handler 对象
handler = urllib.request.HTTPHandler()
# 获取 opener 对象
opener = urllib.request.build_opener(handler)
# 调用 open 方法
response = opener.open(request)
content = response.read().decode('utf-8')
print(content)

解析

xpath 插件的安装

xpath插件:

注意:提前安装xpath插件

  • 打开chrome浏览器
  • 点击右上角小圆点
  • 更多工具
  • 扩展程序
  • 拖拽xpath插件到扩展程序中
  • 如果crx文件失效,需要将后缀修改zip
  • 再次拖拽
  • 关闭浏览器重新打开
  • ctrl + shift + x (关闭与打开)
  • 出现小黑框

在python安装目录中的 Scripts 文件夹中安装库

# 使用 cmd 
# 安装lxml库
pip install lxml -i https://pypi.douban.com/simple

xpath 基本使用

基本语法

1.路径查询
    //:查找所有子孙节点,不考虑层级关系
    / :找直接子节点
2.谓词查询
    //div[@id]
    //div[@id="maincontent"]
3.属性值查询
    //@class
4.模糊查询
    //div[contains(@id, "he")]
    //div[starts‐with(@id, "he")]
5.内容查询
	//div/h1/text()
6.逻辑运算
    //div[@id="head" and @class="s_down"]
    //title | //price

解析数据

# 导入lxml.etree
from lxml import etree
# etree.parse() 解析本地文件
html_tree = etree.parse('XX.html')
# etree.HTML() 服务器响应文件
html_tree = etree.HTML(response.read().decode('utf‐8')

解析本地文件

在本地创建一个 html 文件

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <title>Titletitle>
head>
<body>
    <ul>
        <li id="l1" class="qqq">javali>
        <li id="l2">pythonli>
        <li id="l3">phpli>
    ul>
body>
html>

开始解析

# 导入
from lxml import etree

# xpath 解析本地文件
tree = etree.parse('test.html')

# 查找 ul 下面的 li
# li_list = tree.xpath('//body/ul/li')

# 查找所有 有id 属性的li 标签
# li_list = tree.xpath('//ul/li[@id]')

# 查询id属性为 l1 的li 标签,    text() 是显示内容的
# li_list = tree.xpath('//ul/li[@id="l1"]/text()')

# 查找id为l1的li标签的class 的属性值
li_list = tree.xpath('//ul/li[@id="l1"]/@class')

print(li_list)
print(len(li_list))

获取百度的百度一下文字

import urllib.request
from lxml import etree

url = 'https://www.baidu.com/'

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36'
}

request = urllib.request.Request(url=url,headers=headers)
# 获取网页源码
response = urllib.request.urlopen(request)
content = response.read().decode('utf-8')

# 解析网页源码,获取需要的数据
tree = etree.HTML(content)

result = tree.xpath('//input[@id="su"]/@value')

print(result)

解析站长素材网站(下载图片)

import urllib.request
from lxml import etree

# 请求对象的定制
def create_request(page):
    if(page == 1):
        url = 'https://sc.chinaz.com/tupian/shuaigetupian.html'
    else:
        url = 'https://sc.chinaz.com/tupian/shuaigetupian_'+str(page)+'.html'
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36'
    }
    request = urllib.request.Request(url=url, headers=headers)
    return request

# 获取网页源码
def get_content(request):
    response = urllib.request.urlopen(request)
    content = response.read().decode('utf-8')
    return content


# 下载
def down_load(content):
    # 解析网页源码,找到图片地址和名称
    tree = etree.HTML(content)
    src_list = tree.xpath('//div[@class="tupian-list com-img-txt-list"]/div[@class="item"]/img/@data-original')
    name_list = tree.xpath('//div[@class="tupian-list com-img-txt-list"]/div[@class="item"]/img/@alt')

    for i in range(len(name_list)):
        name = name_list[i]
        src = src_list[i]
        url = 'https:' + src
        urllib.request.urlretrieve(url=url, filename='D:/Dasktop/1/' + name + '.jpg')

if __name__ == '__main__':
    start_page = int(input('请输入起始页码'))
    end_page = int(input('输入结束页码'))
    for page in range(start_page,end_page+1):
        # 请求对象的定制
        request = create_request(page)
        # 获取网页源码
        content = get_content(request)
        # 下载
        down_load(content)

jsonpath 的安装

在python安装目录中的 Scripts 文件夹中安装库

pip install jsonpath

jsonpath 的使用

obj = json.load(open('json文件', 'r', encoding='utf‐8'))
ret = jsonpath.jsonpath(obj, 'jsonpath语法')

学习连接

http://blog.csdn.net/luxideyao/article/details/77802389

解析一个本地的json文件

{ "store": {
    "book": [
      { "category": "修真",
        "author": "六道",
        "title": "坏蛋是怎样练成的",
        "price": 8.95
      },
      { "category": "修改",
        "author": "天蚕土豆",
        "title": "斗破苍穹",
        "price": 12.99
      },
      { "category": "修真",
        "author": "唐家三少",
        "title": "斗罗大陆",
        "isbn": "0-553-21311-3",
        "price": 8.99
      },
      { "category": "修真",
        "author": "南派三叔",
        "title": "星辰变",
        "isbn": "0-395-19395-8",
        "price": 22.99
      }
    ],
    "bicycle": {
      "color": "黑色",
      "price": 19.95
    }
  }
}
import json
import jsonpath

obj = json.load(open('store.json', 'r', encoding='utf-8'))

# 书店里所有书的作者
author = jsonpath.jsonpath(obj, '$.store.book[*].author')

print(author)

jsonpath 解析淘票票

import json
import urllib.request
import jsonpath

url = 'https://dianying.taobao.com/cityAction.json?activityId&_ksTS=1675140524512_128&jsoncallback=jsonp129&action=cityAction&n_s=new&event_submit_doGetAllRegion=true'

headers = {
    'accept': 'text/javascript, application/javascript, application/ecmascript, application/x-ecmascript, */*; q=0.01',
    'accept-language': 'zh-CN,zh;q=0.9',
    'bx-v': '2.2.3',
    'cookie': 'miid=416246555499980443; cna=KOluGRv7Ey8CAXM3Fy0STk5V; t=516e3d641e6ec4105f3aa6584f38a919; cookie2=14e0b11bb8da9614888f0af9b60735b2; v=0; _tb_tok_=783f1edeeb3dd; xlly_s=1; tfstk=c3EOBtjcI6fgisfmYcQHuFl7FIWlaHJtidGvDurZ-WeBn3xvcsc_quHKfNMqafBd.; l=fBaSf-LcTWOww-33BO5Cnurza77tfIRb4sPzaNbMiIEGa6QhtFMY0NCeB7iXSdtjgTCYxetz_ANlDdLHR3xg5c0c07kqm0Rx3xvtaQtJe; isg=BIuL3VeVbIqUyLDecITKIwlTGi91IJ-iLmdX2f2IikohHKt-hfH_8niy9hzyPPea',
    'referer': 'https://dianying.taobao.com/',
    'sec-ch-ua': '"Not_A Brand";v="99", "Google Chrome";v="109", "Chromium";v="109"',
    'sec-ch-ua-mobile': '?0',
    'sec-ch-ua-platform': '"Windows"',
    'sec-fetch-dest': 'empty',
    'sec-fetch-mode': 'cors',
    'sec-fetch-site': 'same-origin',
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36',
    'x-requested-with': 'XMLHttpRequest'
}

request = urllib.request.Request(url=url, headers=headers)
response = urllib.request.urlopen(request)
content = response.read().decode('utf-8')
# 切割元素
content = content.split('(')[1]
content = content.split(')')[0]
fp = open('淘票票.json', 'w', encoding='utf-8')
fp.write(content)
fp.close()

obj = json.load(open('淘票票.json', 'r', encoding='utf-8'))
city_list = jsonpath.jsonpath(obj, '$..regionName')
print(city_list)

wget 下载库

wget 是一个下载文件的库,可以结合 requests 库下载文件

安装

pip insatll wget

使用

import wget
url = '文件路径’
path = 'D:/x.png' # 保存的路径
wget.download(url, path) # 下载

使用 requests库、wget 库下载站长素材网站第一页的图片

import requests
import wget
from lxml import etree

url = 'https://sc.chinaz.com/tupian/shuaigetupian.html'
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36'
}
# 获取网页源码
response = requests.get(url=url, headers=headers)
# 设置源码的编码格式
response.encoding = 'utf-8'
content = response.text

# 解析网页源码,找到图片地址和名称
tree = etree.HTML(content)
src_list = tree.xpath('//div[@class="tupian-list com-img-txt-list"]/div[@class="item"]/img/@data-original')
name_list = tree.xpath('//div[@class="tupian-list com-img-txt-list"]/div[@class="item"]/img/@alt')

for i in range(len(name_list)):
    name = name_list[i]
    src = src_list[i]
    url = 'https:' + src
    # 下载 每一个参数路径,第二个参数 保存的路径
    wget.download(url, 'D:/Dasktop/1/' + name + '.jpg')

requests

文档

官方文档

https://requests.readthedocs.io/projects/cn/zh_CN/latest/

快速上手

https://requests.readthedocs.io/projects/cn/zh_CN/latest/user/quickstart.html

安装

pip install requests

response的一个类型和六个属性

import requests

url = 'https://www.baidu.com'
response = requests.get(url=url)

# 一个类型和六个属性
# 类型 
# print(type(response))

# encoding : 设置响应的编码格式
# response.encoding = 'utf-8'

# text : 以字符串的形式返回网页的源码
# print(response.text)

# url : 获取请求的地址
# print(response.url)

# content : 获取二进制数据(用的不多)
# print(response.content)

# status_code : 获取状态码
print(response.status_code)

# headers : 返回的是响应头
print(response.headers)

get 请求

参数要求

  • 参数使用params传递
  • 参数无需urlencode编码
  • 不需要请求对象的定制
  • 请求资源路径中?可加可不加
import requests

url = 'https://www.baidu.com/s?'

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36'
}

data = {
    'wd': '北京'
}

# 参数解释
# url 请求路径、params 参数、kwargs 字典
response = requests.get(url=url, params=data, headers=headers)

content = response.text
print(content)

post 请求

import requests
import json

url_page = 'https://fanyi.baidu.com/sug'
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36'
}
data = {
    'kw': 'spider'
}

# 参数解释
# url 请求路径、data 参数、kwargs 字典
response = requests.post(url=url_page, data=data, headers=headers)
content = response.text
print(content)

# 转换 json 格式
obj = json.loads(content)
print(obj)

代理

import requests

url = 'http://www.baidu.com/s?'
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 Edg/109.0.1518.70'
}
data = {
    'wd': 'ip'
}
# 快代理(网上的免费代理)
proxies = {
    'https': '121.13.252.60:53281'
}
response = requests.get(url=url, params=data, headers=headers, proxies=proxies)

content = response.text
fp = open('daili.html', 'w', encoding='utf-8')
fp.write(content)
fp.close()

cookie登陆古诗文网

# 登陆所需要的参数
# __VIEWSTATE: jUAS4+BtAMbk/ScjLZKWT3TZPYNDmrN8gDQBD54mD6lANGUIi5x2dnVZd5hnfBjZEvQw5N3Wt0gmvo3LBQGxToQPcEUiXbvq2jzyWoSS2iky7OjitxuhoRZWrsVQBJ10v411HlgLFRTCgaW3C6KOEj7iVxk=
# __VIEWSTATEGENERATOR: C93BE1AE
# from: http://so.gushiwen.cn/user/collect.aspx
# email: 12312312312zz
# 密码字段: xxx
# code: 123
# denglu: 登录


import requests
from lxml import etree

url = 'https://so.gushiwen.cn/user/login.aspx?from=http://so.gushiwen.cn/user/collect.aspx'
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 Edg/109.0.1518.70'
}
# 获取网页源码
response = requests.get(url=url, headers=headers)
content = response.text
tree = etree.HTML(content)
# 获取登陆参数
VIEWSTATE = tree.xpath('//input[@id="__VIEWSTATE"]/@value')[0]
VIEWSTATEGENERATOR = tree.xpath('//input[@id="__VIEWSTATEGENERATOR"]/@value')[0]

# 获取登陆验证码
code = tree.xpath('//img[@id="imgCode"]/@src')[0]
# 获取图片验证码下载到本地,在控制台输入验证码
code_url = 'https://so.gushiwen.cn' + code

# requests.session() 方法 通过session 的返回值,就能使请求变成一个对象
session = requests.session()
# 这里得到 url 的内容
response_code = session.get(code_url)
# 注意此时使用二进制数据,因为要下载图片
content_code = response_code.content
# wb就是将二进制的数据写入文件
fp = open('验证码.jpg', 'wb')
fp.write(content_code)
fp.close()

code_name = input('请输入验证码')

# 开始登陆
url_login = 'https://so.gushiwen.cn/user/login.aspx?from=http%3a%2f%2fso.gushiwen.cn%2fuser%2fcollect.aspx%3ftype%3dm'
data = {
    '__VIEWSTATE': VIEWSTATE,
    '__VIEWSTATEGENERATOR': VIEWSTATEGENERATOR,
    'from': 'http://so.gushiwen.cn/user/collect.aspx',
    'email': 'xxx',
    'pw': 'xxx',
    'code': code_name,
    'denglu': '登录'
}
# 这里使用的是 session 发送的请求,目的就是为了与验证码的请求保持一致,否则验证码哪里将不一致
response_login = session.post(url=url_login, data=data, headers=headers)
content_login = response_login.text

fp = open('gushici.html', 'w', encoding='utf-8')
fp.write(content_login)
fp.close()

scrapy

Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架

可以应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中

scrapy 安装

pip install scrapy -i https://pypi.tuna.tsinghua.edu.cn/simple/

可能出现的报错情况

报错1:报错信息如下

building ‘twisted.test.raiser’ extension
error: Microsoft Visual C++ 14.0 is required. Get it with “Microsoft Visual C++
Build Tools”: http://landinghub.visualstudio.com/visual‐cpp‐build‐tools

解决1
http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
下载 twisted 对应版本的whl文件(如我的Twisted‐17.5.0‐cp36‐cp36m‐win_amd64.whl)
cp后面是python版本,amd是操作系统的版本(amd64代表64)
下载完成之后,执行命令安装twisted
pip install C:\Users\...\Twisted‐17.5.0‐cp36‐cp36m‐win_amd64.whl
安装完twisted之后,再次安装scrapy
pip install scrapy

报错2:报错信息如下(让升级pip)

python ‐m pip install ‐‐upgrade pip

解决2
直接运行命令
python -m pip install --upgrade pip

报错3:

win32 的错误

解决3
运行命令
pip install pypiwin32

创建 scrapy 项目

# 在 cmd 中执行命令行
scrapy startproject 项目名
# 项目名不能以数字开头,并且不能有中文
# eg:scrapy startproject scrapy01

项目的组成

spiders		# 存放爬虫文件的文件夹
    __init__.py   
    自定义的爬虫文件.py   # 由我们自己创建,是实现爬虫核心功能的文件
__init__.py
items.py 	# 定义数据结构的地方,是一个继承自scrapy.Item的类
middlewares.py 		# 中间件 代理
pipelines.py 	# 管道文件,里面只有一个类,用于处理下载数据的后续处理,默认是300优先级,值越小优先级越高(1‐1000)
settings.py 	# 配置文件 比如:是否遵守robots协议,User‐Agent定义等

创建爬虫文件

需要通过 cmd 命令行的方式进行创建,首先进入存放爬虫文件的文件夹(spiders),然后执行命令

scrapy genspider 爬虫文件的名字 要爬取的网页地址
eg: scrapy genspider baidu www.baidu.com

爬虫文件的组成

import scrapy


class BaiduSpider(scrapy.Spider):
    # 爬虫文件的名称
    name = 'baidu'
    # 允许访问的域名
    allowed_domains = ['www.baidu.com']
    # 其实的 url 地址,指的是爬虫运行时第一次要访问的页面
    # start_urls 是 allowed_domains 的前面加一个 http://
    #             allowed_domains 的后面加一个 /
    start_urls = ['http://www.baidu.com/']

    # 执行了 start_url 之后执行的解析数据的方法,方法中的 response 就是返回的对象
    # response 对象相当于
    #           response = urllib.request.urlopen()
    #           response = requests.get()
    #			response.text	响应的是字符串
    #			response.body	响应的是二进制文件
    #			response.xpath('xpath语法')  xpath方法的返回值类型是selector列表
    #			extract() 	提取的是selector对象的是data,结果是列表
    #			extract_first()		提取的是selector列表中的第一个数据,结果是数据
    def parse(self, response):
        print('hello scrapy')

运行爬虫文件

# 在 cmd 命令行运行,一定要在 spiders 文件夹中执行
scrapy crawl 爬虫文件名称
eg:scrapy crawl baidu

robots 协议

robots 协议是一个网站定义的协议,规定了文件是否可以爬取。

查看协议只需要在网站的后面加上 robots.txt

例如百度的协议 :http://www.baidu.com/robots.txt

scrapy 创建的爬虫默认是遵守robots 协议的,

如果不想遵守可以到 settings 文件中第 20 行ROBOTSTXT_OBEY = True将其改为 False 或注释即可

爬取 汽车之家的汽车名称和价格

import scrapy


class CarSpider(scrapy.Spider):
    name = 'car'
    allowed_domains = ['https://car.autohome.com.cn/price/brand-91.html']
    # 如果是以 html 结尾,就不需要加 / 了,否则会报错
    start_urls = ['https://car.autohome.com.cn/price/brand-91.html']

    def parse(self, response):
        name_list = response.xpath('//div[@class="main-title"]/a/text()').extract()
        price_list = response.xpath('//div[@class="main-lever-right"]//span[@class="font-arial"]/text()').extract()
        for i in range(len(name_list)):
            name = name_list[i]
            price = price_list[i]
            print(name, price)

调试工具 scrapy shell

scrapy shell 是一个Scrapy终端,是一个交互终端,供您在未启动spider的情况下尝试及调试您的爬取代码

其本意是用来测试提取数据的代码,不过您可以将其作为正常的Python终端,在上面测试任何的Python代码

该终端是用来测试XPath或CSS表达式,查看他们的工作方式及从爬取的网页中提取的数据。 在编写您的spider时,

该终端提供了交互性测试您的表达式代码的功能,免去了每次修改后运行spider的麻烦

安装 ipython

pip install ipython

当当网

单条管道

爬虫脚本dangdnag.py

import scrapy
# 导入项目中的 items
from scrapy04.items import Scrapy04Item

class DangdangSpider(scrapy.Spider):
    name = 'dangdang'
    allowed_domains = ['https://category.dangdang.com/cp01.01.02.00.00.00.html']
    start_urls = ['https://category.dangdang.com/cp01.01.02.00.00.00.html']

    def parse(self, response):
        # 获取书的图片、名称、价格等数据
        li_list = response.xpath('//ul[@id="component_59"]/li')
        for li in li_list:
            src = li.xpath('.//img/@data-original').extract_first()
            if src:
                src = src
            else:
                src = li.xpath('.//img/@src').extract_first()
            name = li.xpath('.//img/@alt').extract_first()
            price = li.xpath('.//span[@class="search_now_price"]/text()').extract_first()
            print(src, name, price)

            # 封装一个图书的items,定义数据结构
            book = Scrapy04Item(src=src, name=name, price=price)

            # 获取一个 book 就将book交给管道 pipelines,类似于下载了
            yield book

定义数据结构 items

import scrapy


class Scrapy04Item(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()

    # 图书图片地址
    src = scrapy.Field()

    # 图书名称
    name = scrapy.Field()

    # 图书的价格
    price = scrapy.Field()

管道 pipelines.py

from itemadapter import ItemAdapter

# 如果要使用管道,就要在 settings 中开启管道,65的注释解开
class Scrapy04Pipeline:
    # 在爬虫文件执行之前执行的方法
    def open_spider(self, spider):
        self.fp = open('book.json', 'w', encoding='utf-8')

    # item 就是 yield 后面的 book 对象
    def process_item(self, item, spider):
        # # write() 只能写一个字符串,不能写 json
        # # w 会覆盖文件之前的内容,只写本次的内容,a 可以在文件中追加内容
        # with open('book.json', 'a', encoding='utf-8') as fp:
        #     fp.write(str(item))

        # 上面的这种方式不推荐使用,每次传过来一次文件,就要打开一次文件,对文件操作过于频繁
        # 这里我们需要借助 open_spider 和 close_spider 两个方法
        self.fp.write(str(item))
        return item

    # 在爬虫文件执行之后执行的方法
    def close_spider(self, spider):
        self.fp.close()

多管道

在单管道里面修改

管道 pipelines

from itemadapter import ItemAdapter
import wget

# 如果要使用管道,就要在 settings 中开启管道,65的注释解开
class Scrapy04Pipeline:
    # 在爬虫文件执行之前执行的方法
    def open_spider(self, spider):
        self.fp = open('book.json', 'w', encoding='utf-8')

    # item 就是 yield 后面的 book 对象
    # 每次都会执行该方法
    def process_item(self, item, spider):
        # # write() 只能写一个字符串,不能写 json
        # # w 会覆盖文件之前的内容,只写本次的内容,a 可以在文件中追加内容
        # with open('book.json', 'a', encoding='utf-8') as fp:
        #     fp.write(str(item))

        # 上面的这种方式不推荐使用,每次传过来一次文件,就要打开一次文件,对文件操作过于频繁
        # 这里我们需要借助 open_spider 和 close_spider 两个方法
        self.fp.write(str(item))
        return item

    # 在爬虫文件执行之后执行的方法
    def close_spider(self, spider):
        self.fp.close()

# 下载图片的管道
# 去 settings 中设置优先级
class DangDownloadPicPipeline:
    # item 就是 yield 后面的 book 对象
    def process_item(self, item, spider):
        url = 'http:' + item.get('src')
        filename = './books/' + item.get('name') + '.jpg'
        wget.download(url, filename)
        return item

settings 配置文件 设置管道优先级

ITEM_PIPELINES = {
   'scrapy04.pipelines.Scrapy04Pipeline': 300,
   'scrapy04.pipelines.DangDownloadPicPipeline': 301,
}

多页数据下载

思路:每个页面执行一次 parse 方法就行了

在多管道的基础上修改

爬虫文件 dangdang.py

import scrapy
# 导入项目中的 items
from scrapy04.items import Scrapy04Item

class DangdangSpider(scrapy.Spider):
    name = 'dangdang'
    # 如果是多页下载的话,要调整 allowed_domains 的范围,一般只写域名
    allowed_domains = ['category.dangdang.com']
    start_urls = ['https://category.dangdang.com/cp01.01.02.00.00.00.html']

    base_url = 'https://category.dangdang.com/pg'
    page = 1


    def parse(self, response):
        # 获取书的图片、名称、价格等数据
        li_list = response.xpath('//ul[@id="component_59"]/li')
        for li in li_list:
            src = li.xpath('.//img/@data-original').extract_first()
            if src:
                src = src
            else:
                src = li.xpath('.//img/@src').extract_first()
            name = li.xpath('.//img/@alt').extract_first()
            price = li.xpath('.//span[@class="search_now_price"]/text()').extract_first()
            print(src, name, price)

            # 封装一个图书的items,定义数据结构
            book = Scrapy04Item(src=src, name=name, price=price)

            # 获取一个 book 就将book交给管道 pipelines,类似于下载了
            yield book


        # 每页爬取的业务逻辑都是一样的,只需要将每页的请求调用一下 parse 方法即可
        if self.page < 10:
            self.page = self.page + 1
            url = self.base_url + str(self.page) + '-cp01.01.02.00.00.00.html'
            # 调用 parse 方法
            yield scrapy.Request(url=url, callback=self.parse)

电影天堂多页数据下载json(图片与标题)

图片与标题不在一个页面

爬虫文件mv.py

import scrapy
from scrapy05_movie.items import Scrapy05MovieItem

class MvSpider(scrapy.Spider):
    name = 'mv'
    allowed_domains = ['www.dytt8.net']
    start_urls = ['https://www.dytt8.net/html/gndy/china/index.html']

    def parse(self, response):
        # 要第一页的名字和第二页的图片
        a_list = response.xpath('//div[@class="co_content8"]//td[2]//a[2]')

        for a in a_list:
            # 第一页的名字和要点击的链接
            name = a.xpath('./text()').extract_first()
            href = a.xpath('./@href').extract_first()

            # 第二页的地址
            url = 'https://www.dytt8.net' + href
            # 对第二页链接发起访问
            yield scrapy.Request(url=url, callback=self.parse_second, meta={'name': name})

    def parse_second(self, response):
        src = response.xpath('//div[@id="Zoom"]//img/@src').extract_first()
        name = response.meta['name']
        # 封装对象
        movie = Scrapy05MovieItem(name=name, src=src)
        yield movie

定制对象 items.py

import scrapy


class Scrapy05MovieItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()

    # 名字
    name = scrapy.Field()

    # 地址
    src = scrapy.Field()

编写管道 pipelines

from itemadapter import ItemAdapter

# 如果要使用管道,就要在 settings 中开启管道,65的注释解开
class Scrapy05MoviePipeline:
    def open_spider(self, spider):
        self.fp = open('./movie.json', 'w', encoding='utf-8')

    def process_item(self, item, spider):
        self.fp.write(str(item))
        return item

    def close_spider(self, spider):
        self.fp.close()

pymysql 使用步骤

pip install pymysql -i https://pypi.tuna.tsinghua.edu.cn/simple/
pymysql.connect(host,port,user,password,db,charset)
conn.cursor()
cursor.execute()

链接提取器 CrawlSpider

继承自scrapy.Spider

CrawlSpider可以定义规则,再解析html内容的时候,可以根据链接规则提取出指定的链接,然后再向这些链接发送请求

所以,如果有需要跟进链接的需求,意思就是爬取了网页之后,需要提取链接再次爬取,使用CrawlSpider是非常合适的

案例:CrawlSpider 读书网 并存入MySQL数据库

1、创建项目

scrapy startproject scrapy06_read

2、进入爬虫文件夹 spiders

cd scrapy06_read/scrapy06_read/spiders

3、创建爬虫文件

scrapy genspider -t crawl read www.dushu.com

4、编写爬虫文件内容

import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from scrapy06_read.items import Scrapy06ReadItem

class ReadSpider(CrawlSpider):
    name = 'read'
    allowed_domains = ['www.dushu.com']
    start_urls = ['http://www.dushu.com/book/1188_1.html']

    rules = (
        # follow=true 是否跟进 就是按照提取连接规则进行提取
        Rule(LinkExtractor(allow=r'/book/1188_\d+\.html'), callback='parse_item', follow=True),
    )

    def parse_item(self, response):
        img_list = response.xpath('//div[@class="bookslist"]//img')

        for img in img_list:
            src = img.xpath('./@data-original').extract_first()
            name = img.xpath('./@alt').extract_first()
            book = Scrapy06ReadItem(name=name, src=src)
            yield book

5、定制对象

import scrapy

class Scrapy06ReadItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    name = scrapy.Field()
    src = scrapy.Field()

6、开启管道

from itemadapter import ItemAdapter

# 如果要使用管道,就要在 settings 中开启管道,65的注释解开
class Scrapy06ReadPipeline:
    def open_spider(self, spider):
        self.fp = open('book.json', 'w', encoding='utf-8')

    def process_item(self, item, spider):
        self.fp.write(str(item))
        return item

    def close_spider(self,spider):
        self.fp.close()

7、运行爬虫

scrapy crawl read

8、创建数据库表

/*
 Navicat Premium Data Transfer

 Source Server         : mysql
 Source Server Type    : MySQL
 Source Server Version : 80025
 Source Host           : localhost:3306
 Source Schema         : spider

 Target Server Type    : MySQL
 Target Server Version : 80025
 File Encoding         : 65001

 Date: 02/02/2023 16:29:20
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for book
-- ----------------------------
DROP TABLE IF EXISTS `book`;
CREATE TABLE `book`  (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `src` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of book
-- ----------------------------

SET FOREIGN_KEY_CHECKS = 1;

9、进入项目中的 settings.py 文件随便找个地方设置数据库的配置信息

# MySQL 主机地址
DB_HOST = '127.0.0.1'
# MySQL 端口号(int类型的)
DB_PORT = 3306
# 数据库用户名
DB_USER = 'root'
# 数据库密码
DB_PASSWORD = 'admin'
# 数据库名称
DB_NAME = 'spider'
# 数据库的编码格式,不允许写 utf-8 中的 -
DB_CHARSET = 'utf8'

10、修改管道 piplines.py 的内容

settings.py 中配置一下管道(用的是双管道)

ITEM_PIPELINES = {
   'scrapy06_read.pipelines.Scrapy06ReadPipeline': 300,
   'scrapy06_read.pipelines.MysqlPipline': 301,
}

piplines.py 内容

from itemadapter import ItemAdapter
# 加载settings文件
from scrapy.utils.project import get_project_settings
# 导入 pymysql
import pymysql

# 如果要使用管道,就要在 settings 中开启管道,65的注释解开
class Scrapy06ReadPipeline:
    def open_spider(self, spider):
        self.fp = open('book.json', 'w', encoding='utf-8')

    def process_item(self, item, spider):
        self.fp.write(str(item))
        return item

    def close_spider(self,spider):
        self.fp.close()

# 存入数据库
class MysqlPipline:
    def open_spider(self, spider):
        settings = get_project_settings()
        self.host = settings['DB_HOST']
        self.port = settings['DB_PORT']
        self.user = settings['DB_USER']
        self.password = settings['DB_PASSWORD']
        self.name = settings['DB_NAME']
        self.charset = settings['DB_CHARSET']

        # 连接 MySQL
        self.connect()

    def connect(self):
        self.conn = pymysql.connect(
            host=self.host,
            port=self.port,
            user=self.user,
            password=self.password,
            db=self.name,
            charset=self.charset
        )
        self.cursor = self.conn.cursor()

    def process_item(self, item, spider):

        sql = 'insert into book(name,src) values("{}","{}")'.format(item['name'], item['src'])

        # 执行 SQL
        self.cursor.execute(sql)
        # 提交事务
        self.conn.commit()

        return item

    def close_spider(self, spider):
        self.cursor.close()
        self.conn.close()

日志信息 和 日志级别

CRITICAL:严重错误
ERROR: 一般错误
WARNING: 警告
INFO: 一般信息
DEBUG: 调试信息(低,默认)

只要出现了DEBUG或者DEBUG以上等级的日志
那么这些日志将会打印

在cmd 中不显示日志有两种方式:

  • 设置日志等级(基本不使用)
  • 设置日志输出地址(常用)

一般选择第二种,因为第一种会省略日志信息,如果项目报错了,不方便查看错误信息,而第二种方式则会将日志信息输出到一个文件里,cmd 命令行中依然不提示任何日志信息,日志信息可以通过 日志文件查看,这样即解决了cmd中显示日志的问题,有保存了日志信息方便排查代码错误

设置日志等级

在settings.py 文件中任意地方加上

LOG_LEVEL : 设置日志显示的等级,就是显示哪些,不显示哪些
eg:
LOG_LEVEL='WARNING'

设置日志输出地址

LOG_FILE : 将屏幕显示的信息全部记录到文件中,屏幕不再显示,注意文件后缀一定是.log
eg:
LOG_FILE='logdemo.log'

scrapy 的 post 请求

请求百度翻译

post请求 和 start_urls 、parse方法没有关系,使用的是 start_requests()方法

import scrapy
import json

class TestpostSpider(scrapy.Spider):
    name = 'testpost'
    allowed_domains = ['https://fanyi.baidu.com/sug']
    # post请求 和 start_urls 、parse方法没有关系,使用的是 start_requests()方法
    # start_urls = ['https://fanyi.baidu/com/sug/']
    #
    # def parse(self, response):
    #     pass

    def start_requests(self):
        url = 'https://fanyi.baidu.com/sug'

        data = {
            'kw': 'final'
        }

        yield scrapy.FormRequest(url=url, formdata=data, callback=self.parse_second)

    def parse_second(self, response):
        content = response.text
        print(content)
        obj = json.loads(content)
        print(obj)

扩展

创建文件夹

import os
# 创建一级目录,如果1文件夹不存在,则会报错
os.mkdir('D:/1/2')
# 创建多级目录
os.makedirs('D:/1/2')

你可能感兴趣的:(python,爬虫,python,开发语言)