# 使用 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)
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)
应用场景 :路径中有多个参数时,需要用到该方法。将多个参数变为 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
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 方法
# 参数是放在请求对象定制的方法中
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)
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()
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 可以定制高级的请求头
随着业务逻辑的复杂 请求对象的定制已经满足不了我们的需求(动态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插件
# 使用 cmd
# 安装lxml库
pip install lxml -i https://pypi.douban.com/simple
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)
在python安装目录中的 Scripts 文件夹中安装库
pip install jsonpath
obj = json.load(open('json文件', 'r', encoding='utf‐8'))
ret = jsonpath.jsonpath(obj, 'jsonpath语法')
http://blog.csdn.net/luxideyao/article/details/77802389
{ "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)
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 是一个下载文件的库,可以结合 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')
官方文档
https://requests.readthedocs.io/projects/cn/zh_CN/latest/
快速上手
https://requests.readthedocs.io/projects/cn/zh_CN/latest/user/quickstart.html
pip install requests
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)
参数要求
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)
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()
# 登陆所需要的参数
# __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是一个为了爬取网站数据,提取结构性数据而编写的应用框架
可以应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中
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
# 在 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.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终端,是一个交互终端,供您在未启动spider的情况下尝试及调试您的爬取代码
其本意是用来测试提取数据的代码,不过您可以将其作为正常的Python终端,在上面测试任何的Python代码
该终端是用来测试XPath或CSS表达式,查看他们的工作方式及从爬取的网页中提取的数据。 在编写您的spider时,
该终端提供了交互性测试您的表达式代码的功能,免去了每次修改后运行spider的麻烦
pip install ipython
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
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()
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()
在单管道里面修改
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
ITEM_PIPELINES = {
'scrapy04.pipelines.Scrapy04Pipeline': 300,
'scrapy04.pipelines.DangDownloadPicPipeline': 301,
}
思路:每个页面执行一次 parse 方法就行了
在多管道的基础上修改
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)
图片与标题不在一个页面
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
import scrapy
class Scrapy05MovieItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
# 名字
name = scrapy.Field()
# 地址
src = scrapy.Field()
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()
pip install pymysql -i https://pypi.tuna.tsinghua.edu.cn/simple/
pymysql.connect(host,port,user,password,db,charset)
conn.cursor()
cursor.execute()
继承自scrapy.Spider
CrawlSpider可以定义规则,再解析html内容的时候,可以根据链接规则提取出指定的链接,然后再向这些链接发送请求
所以,如果有需要跟进链接的需求,意思就是爬取了网页之后,需要提取链接再次爬取,使用CrawlSpider是非常合适的
scrapy startproject scrapy06_read
cd scrapy06_read/scrapy06_read/spiders
scrapy genspider -t crawl read www.dushu.com
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
import scrapy
class Scrapy06ReadItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
name = scrapy.Field()
src = scrapy.Field()
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()
scrapy crawl read
/*
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;
# 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'
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'
请求百度翻译
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')