from urllib import request
resp=request.urlopen('http://www.baidu.com')
print(resp.read())
read()读取所有数据
readline()只读取一行
readlines()逐行列表形势读取
request.urlretrieve('http://www.xxx.com','index.html')
将网页中的内容保存到本地文件中
from urllib import parse
params={'name':'xx','age':18,'greet':'hellow ddd'}
resp=parse.urlencode(params)
#将params编码
print(resp)
aa=parse.parse_qs(resp)
#将resp解码
print(aa)
搜索刘德华
from urllib import request
from urllib import parse
import ssl
#增加ssl证书验证,没有这句话报错
ssl._create_default_https_context = ssl._create_unverified_context
url='http://www.baidu.com/baidu'
params={'wd':'刘德华'}
#将汉字刘德华编码,得到整体url
qs=parse.urlencode(params)
print(qs)
url=url+'?'+qs
print(url)
resp=request.urlopen(url)
print(resp.read())
url解析的两个函数
from urllib import parse
url='http://www.baidu.com/s?wd=python&uname=admin#1'
#两种获得参数的函数
result1=parse.urlparse(url)
result2=parse.urlsplit(url)
print(result1)
print(result2)
#得到结果如下
ParseResult(scheme='http', netloc='www.baidu.com', path='/s', params='', query='wd=python&uname=admin', fragment='1')
SplitResult(scheme='http', netloc='www.baidu.com', path='/s', query='wd=python&uname=admin', fragment='1')
# encoding utf8
from urllib import request, parse
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
url = 'https://www.lagou.com/jobs/list_python?labelWords=&fromSearch=true&suginput='
#添加请求头部信息
headers = {
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) '
'Chrome/83.0.4103.61 Safari/537.36 '
}
req = request.Request(url, headers=headers)
resp = request.urlopen(req)
print(resp.read().decode('utf-8'))
# encoding utf-8
from urllib import request
url = 'http://httpbin.org/ip'
# resp=request.urlopen(url)
# print(resp.read())
#创建一个handler使用代理http或者https
handler = request.ProxyHandler({'http': '60.217.64.237:38829'})
opener = request.build_opener(handler)
resp = opener.open(url)
print(resp.read())
#write 必须写入一个str数据类型
#resp.read()读出来是一个bytes数据类型
#bytes-> decode -> str
#str-> encode -> bytes
其中涉及到cookie的保存
# encoding utf-8
from urllib import request, parse
from http.cookiejar import CookieJar
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:76.0) Gecko/20100101 Firefox/76.0'
}
data = {
'email': '自己的手机号或者邮箱',
'password': '自己的密码'
}
def get_opener():
# 创建cookiejar
cookiejar = CookieJar()
# 创建handler
handler = request.HTTPCookieProcessor(cookiejar)
# 创建opener
opener = request.build_opener(handler)
return opener
def login_renren(opener):
login_url = 'http://www.renren.com/PLogin.do'
req = request.Request(login_url, headers=headers, data=parse.urlencode(data).encode('utf-8'))
opener.open(req)
#此处为大鹏的人人网地址
def visit_profile(opener):
dapeng_url = 'http://www.renren.com/人人id/profile'
resp = opener.open(dapeng_url)
with open('renren.html', 'w', encoding='utf-8') as f:
f.write(resp.read().decode('utf-8'))
if __name__ == '__main__':
opener = get_opener()
login_renren(opener)
visit_profile(opener)
#encoding utf-8
from urllib import request
from http.cookiejar import MozillaCookieJar
cookiejar=MozillaCookieJar('cookie.txt')
handler=request.HTTPCookieProcessor(cookiejar)
opener=request.build_opener(handler)
url='http://httpbin.org/cookies/set?course=abc'
resp=opener.open(url)
#其中的ignore_discard=True是为了让即将过期或者已经过期的cookie能够显示
cookiejar.save(ignore_discard=True)
#encoding utf-8
from urllib import request
from http.cookiejar import MozillaCookieJar
cookiejar=MozillaCookieJar('cookie.txt')
#在这里添加ignore_discard=True
cookiejar.load(ignore_discard=True)
handler=request.HTTPCookieProcessor(cookiejar)
opener=request.build_opener(handler)
url='http://httpbin.org/cookies'
for cookie in cookiejar:
print(cookie)
# endcoding utf-8
import requests
resp = requests.get('https://www.baidu.com/')
print(resp.content)
print(resp.url)
print(resp.status_code)
print(resp.encoding)
# encoding utf-8
import requests
proxy = {
'http': '182.46.251.204:9999'
}
resp = requests.get('http://httpbin.org/ip', proxies=proxy)
print(resp.content)
#添加verify=False绕过ssl证书验证
resp = requests.get('http://httpbin.org/ip', proxies=proxy,verify=False)
nodename 选取此节点的所有子节点
/ 从当前节点选取直接子节点
// 从当前节点选取子孙节点
. 选取当前节点
.. 选取当前节点的父节点
@ 选取属性
获取所有的tr标签
# encoding utf-8
from lxml import etree
parser = etree.HTMLParser(encoding='utf-8')
html = etree.parse('test.html', parser=parser)
# 获取所有的tr标签
# xpath函数返回的为列表
trs = html.xpath('//tr')
for tr in trs:
print(etree.tostring(tr, encoding='utf-8').decode('utf-8'))
获取第二个tr标签
tr = html.xpath('//tr[2]')
print(etree.tostring(tr, encoding='utf-8').decode('utf-8'))
获取所有类名为even的数据
trs=html.xpath('//tr[@class=even]')
print(etree.tostring(trs, encoding='utf-8').decode('utf-8'))
获取a标签的href属性
aList=html.xpath('//a/@href')
for a in aList:
print(etree.tostring(a, encoding='utf-8').decode('utf-8'))
#获取tr标签下所有a的href属性
fullurl=tr.xpath('.//a/@href')
#获取tr标签下的文字
title=tr.xpath('./td[1]//text()')
# encoding utf-8
import requests
from lxml import etree
BASE_DOMAIN = 'https://www.dytt8.net'
HEADERS = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36',
'Referer': 'https://www.dytt8.net/html/gndy/dyzz/list_23_2.html'
}
def get_detail_urls(url):
resp = requests.get(url, headers=HEADERS)
text = resp.text
html = etree.HTML(text)
detail_urls = html.xpath("//table[@class='tbspan']//a/@href")
detail_urls = map(lambda url: BASE_DOMAIN + url, detail_urls)
return detail_urls
def parse_detail_page(url):
movie = {}
response = requests.get(url, headers=HEADERS)
text = response.content.decode('gbk')
html = etree.HTML(text)
title = html.xpath("//font[@color='#07519a']/text()")[0]
movie['title'] = title
zoomE = html.xpath("//div[@id='Zoom']")[0]
cover = zoomE.xpath(".//img/@src")[0]
movie['cover'] = cover
def parse_info(info, rule):
return info.replace(rule, "").strip()
infos = zoomE.xpath(".//text()")
# 此处enumerate函数可以返回除了基本信息以外的索引号
for index, info in enumerate(infos):
# print(info)
# print(index)
# print("=" * 10)
if info.startswith('◎年 代'):
info = parse_info(info, "◎年 代")
movie['year'] = info
elif info.startswith('◎产 地'):
info = parse_info(info, "◎产 地")
movie['country'] = info
elif info.startswith('◎类 别'):
info = parse_info(info, "◎类 别")
movie['category'] = info
elif info.startswith('◎上映日期'):
info = parse_info(info, "◎上映日期")
movie['show_time'] = info
elif info.startswith('◎豆瓣评分'):
info = parse_info(info, "◎豆瓣评分")
movie['douban_rating'] = info
elif info.startswith('◎片 长'):
info = parse_info(info, "◎片 长")
movie['duration'] = info
elif info.startswith('◎导 演'):
info = parse_info(info, "◎导 演")
movie['director'] = info
elif info.startswith('◎主 演'):
info = parse_info(info, '◎主 演')
actors = [info]
# 此时在主演以下所有的演员列出来,写一个条件语句,将在为符号处切割来
for x in range(index + 1, len(infos)):
actor = infos[x].strip()
if actor.startswith('◎'):
break
actors.append(actor)
elif info.startswith('◎简 介'):
info = parse_info(info, '◎简 介')
for x in range(index + 1, len(infos)):
profile = infos[x].strip()
if profile.startswith('【下载地址】'):
break
movie['profile'] = profile
download_url = html.xpath("//td[@bgcolor='#fdfddf']//a/@href")[0]
movie['download_url'] = download_url
return movie
def spider():
base_url = 'https://www.dytt8.net/html/gndy/dyzz/list_23_{}.html'
movies = []
for x in range(1, 8):
url = base_url.format(x)
detail_urls = get_detail_urls(url)
for detail_url in detail_urls:
movie = parse_detail_page(detail_url)
movies.append(movie)
print(movies)
if __name__ == '__main__':
spider()
#此处enumerate函数可以返回除了基本信息以外的索引号
for index, info in enumerate(infos):
print(info)
print(index)
def parse_info(info, rule):
return info.replace(rule, "").strip()
上述代码中为将rule替换为空字符串,然后将字符串两侧的空格去除
\w 匹配字母数字及下划线
\W 匹配非字母数字及下划线
\s 匹配任意空白字符,等价于 [\t\n\r\f].
\S 匹配任意非空字符
\d 匹配任意数字,等价于 [0-9].
\D 匹配任意非数字
[...] 用来表示一组字符,单独列出:[amk] 匹配 'a','m'或'k'
[^...] 不在[]中的字符:[^abc] 匹配除了a,b,c之外的字符。
# 点匹配任意字符
import re
text='hellow'
ret=re.match('.',text)
print(ret.group())
import re
#验证手机号码
text='15888888888'
#$符号代表末尾
ret=re.match('1[3578]\d{9}$',text)
print(ret.group())
import re
#验证邮箱
text='[email protected]'
ret=re.match('\w+@[a-z]+\.[a-z]+',text)
print(ret.group())
import re
text='https://www.runoob.com/python/python-reg-expressions.html'
ret=re.match('(http|https|ftp)://[^\s]+',text)
print(ret.group())
import re
text='370982199909246666'
ret=re.match('\d{17}[\dxX]',text)
print(ret.group())
在函数中^表示以。。。开始,在中括号中表示取反
import re
#贪婪模式
text='标题
'
ret=re.match('<.+>',text)
print(ret.group())
#打印结果为:标题
import re
#非贪婪模式
text='标题
'
#在匹配过程中最后添加?号
ret=re.match('<.+?>',text)
print(ret.group())
#打印结果为:
import re
#匹配0-100之间的数字
text='100'
#其中第一位为1-9,第二位有或者没有,所以加'?',但是如果是多位的话还可以匹配,所以加$,两位数字以后结尾或者是结果为100时可以匹配
ret=re.match('[1-9]\d?$|100$',text)
print(ret.group())
import re
text='The price of apples is $99 and the price of oranges is $10'
ret=re.match('.*(\$\d+).*(\$\d+)',text)
#打印正则表达式匹配的所有字符
print(ret.group())
#打印正则表达式中第一个圆括号中匹配的字符
print(ret.group(1))
#打印正则表达式中第二个圆括号中匹配的字符
print(ret.group(2))
#打印正则表达式中所有子分组中匹配的字符
print(ret.groups())
#打印正则表达式中第一个圆括号和第二个圆括号中匹配的字符
print(ret.group(1,2))
#运行结果
The price of apples is $99 and the price of oranges is $10
$99
$10
('$99', '$10')
('$99', '$10')
找出所有满足条件的,返回的为列表
import re
text='The price of apples is $99 and the price of oranges is $10'
ret=re.findall('\$\d+',text)
print(ret)
#打印结果为
['$99', '$10']
sub('正则表达式','你想替换成什么字符串','替换的文本','替换的个数')
将你匹配出的字符串替换成你想要的字符串
import re
text='The price of apples is $99 and the price of oranges is $10'
ret=re.sub('\$\d+',"0",text)
print(ret)
import re
text='hellow wor adv'
ret=re.split(' ',text)
print(ret)
对于经常用到的正则表达式可以先用compile函数编译,后期直接调用,提高性能
import re
#添加了flag参数 re.VERBOSE后,可以在complie中添加注释
r = re.compile(r"""
\d+ #小数点前面
\.? #小数点本身
\d* #小数点后面
""",re.VERBOSE)
text = 'the number is 20.30'
ret = re.findall(r, text)
print(ret)
Python的正则表达式模块re,有一个re.DOTALL的参数。默认情况下,正则表达式中的dot(.),表示所有除了换行的字符,加上re.DOTALL参数后,就是真正的所有字符了,包括换行符(\n)
dumps输出为json格式的字符串
dump输出为json文本,与文件操作并行
# encoding utf-8
import json
data = {'username':'李华','sex':'male','age':16}
# in_json = json.dumps(data)
# print(in_json)
#ensure_ascii=False 这个鱼文件操作中encoding='utf-8'合并使用,可以解析中文
with open('abc.json','w',encoding='utf-8') as f:
json.dump(data,f,ensure_ascii=False)
json.loads()解码python json格式
json.load()加载python json格式文件
import json
json_str = '{"username": "李华", "sex": "male", "age": 16}'
# 将json格式的字符串转为python数据类型的对象
jsonData = json.loads(json_str)
print(jsonData)
print(type(jsonData))
# 加载json文件
with open('abc.json', 'r',encoding='utf-8') as f:
data = json.load(f)
print(data)
# 字典类型
print(type(data))
csv.reader()用法
#reader为一个迭代器,可以遍历,x为列表形式
reader=csv.reader(f)
#next函数可以遍历第一行,如此在for循环中从index=1开始
next(reader)
for x in reader:
print(x)
print(x[1])
csv.DictReader()用法:
reader=csv.DictReader(f)
for x in reader:
print(x)
print(x[1])
# encoding utf-8
import csv
#当文件为元组类型时,用demo1方法写入
def csv_write_demo1():
headers = ['username', 'age', 'height']
values = [
('张三', '18', 175),
('阿娇回家', '19', 175),
('张以', '18', 175),
]
# newline为去除换行,如果没有newline参数,则回添加'\n'
with open('csv_test.csv', 'w', encoding='utf-8', newline='') as f:
writer = csv.writer(f)
writer.writerow(headers)
writer.writerows(values)
#当文件为字典类型时。用demo2方法
def csv_write_demo2():
headers = ['username', 'age', 'height']
values = [
{'username': '张三', 'age': 18, 'height': 190},
{'username': '张d', 'age': 14, 'height': 190},
{'username': '张dg', 'age': 18, 'height': 190},
{'username': '张大概', 'age': 18, 'height': 190},
]
with open('111.csv','w',encoding='utf-8',newline='') as f:
writer=csv.DictWriter(f,headers)
#此处为写入头部信息,虽然上面传入了headers数据,但是需要以下代码写入
writer.writeheader()
writer.writerows(values)
if __name__ == '__main__':
csv_write_demo2()
import time
def coding():
for x in range(3):
print('正在写代码')
time.sleep(1)
def drawing():
for x in range(3):
print('正在画图中')
time.sleep(1)
def main():
coding()
drawing()
if __name__ == '__main__':
main()
#打印结果
#正在写代码
#正在写代码
#正在写代码
#正在画图中
#正在画图中
#正在画图中
#共耗时6s中
开启多线程使用threading,代码如下
#encoding utf-8
import threading
import time
def coding():
for x in range(3):
print('正在写代码')
time.sleep(1)
def drawing():
for x in range(3):
print('正在画图中')
time.sleep(1)
def main():
tr1=threading.Thread(target=coding)
tr2=threading.Thread(target=drawing)
tr1.start()
tr2.start()
if __name__ == '__main__':
main()
#打印结果如下
#正在写代码
#正在画图中
#正在写代码
#正在画图中
#正在画图中
#正在写代码
#共耗时3s
通过以上比较,多线程提高效率,一倍多
#encoding utf-8
import threading
import time
class CodingThread(threading.Thread):
def run(self):
for x in range(3):
print('正在写代码')
time.sleep(1)
class DrawingThread(threading.Thread):
def run(self):
for x in range(3):
print('正在画图中')
time.sleep(1)
def main():
tr1 = CodingThread()
tr2 = DrawingThread()
tr1.start()
tr2.start()
if __name__ == '__main__':
main()
#当前线程的名称
threading.current_thread()
#查看当前线程总数
threading.enumerate()
变量锁只有在更改全局变量时启用,访问时无需添加线程机制锁
# encoding utf-8
import threading
VALUE = 0
#建立全局锁
gLock = threading.Lock()
def add_value():
global VALUE
#开启锁
gLock.acquire()
for x in range(1000000):
VALUE += 1
#释放锁
gLock.release()
print(VALUE)
def main():
for x in range(2):
tr = threading.Thread(target=add_value)
tr.start()
if __name__ == '__main__':
main()
知识点
1、想要在函数中使用全局变量,需要添加global属性
2、Consumer(name=‘生产者线程%d’ % x)name为添加线程名称
# encoding utf-8
import threading
import random
import time
gMoney = 1000
gLock = threading.Lock()
gTotalTimes = 10
gTimes = 0
class Producter(threading.Thread):
def run(self):
#想要在函数中使用全局变量,需要添加global属性
global gMoney
global gTimes
while True:
money = random.randint(100, 1000)
gLock.acquire()
if gTimes >= 10:
#如果在此处不添加释放锁,则会导致锁无法释放,卡死在这
gLock.release()
break
gMoney += money
gTimes += 1
print('%s生产了%d元钱,剩余%d元钱' % (threading.current_thread(), money, gMoney))
gLock.release()
time.sleep(0.5)
class Consumer(threading.Thread):
def run(self):
global gMoney
while True:
money = random.randint(100, 1000)
gLock.acquire()
if gMoney >= money:
gMoney -= money
print('%s消费了%d元钱,剩余%d元钱' % (threading.current_thread(), money, gMoney))
else:
if gTimes >= gTotalTimes:
gLock.release()
break
print('%s消费%d元钱,剩余%d元钱,不足!' % (threading.current_thread(), money, gMoney))
gLock.release()
time.sleep(0.5)
def main():
for x in range(3):
#添加name属性,添加线程名称
tr = Consumer(name='生产者线程%d' % x)
tr.start()
for x in range(5):
tr = Producter(name='消费者线程%d' % x)
tr.start()
if __name__ == '__main__':
main()
# encoding utf-8
import threading
import random
import time
gMoney = 1000
gCondition = threading.Condition()
gTotalTimes = 10
gTimes = 0
class Producter(threading.Thread):
def run(self):
global gMoney
global gTimes
while True:
money = random.randint(100, 1000)
gCondition.acquire()
if gTimes >= 10:
gCondition.release()
break
gMoney += money
gTimes += 1
print('%s生产了%d元钱,剩余%d元钱' % (threading.current_thread(), money, gMoney))
gCondition.notify_all()
gCondition.release()
time.sleep(0.5)
class Consumer(threading.Thread):
def run(self):
global gMoney
while True:
money = random.randint(100, 1000)
gCondition.acquire()
while gMoney < money:
if gTimes >= gTotalTimes:
gCondition.release()
# 此处不可放break,如果放break的话只能退出当前if循环,但是如果是return的话,则会返回整个函数
return
print('%s消费%d元钱,剩余%d元钱,不足!' % (threading.current_thread(), money, gMoney))
gCondition.wait()
gMoney -= money
print('%s消费了%d元钱,剩余%d元钱' % (threading.current_thread(), money, gMoney))
gCondition.release()
time.sleep(0.5)
def main():
for x in range(3):
tr = Consumer(name='生产者线程%d' % x)
tr.start()
for x in range(5):
tr = Producter(name='消费者线程%d' % x)
tr.start()
if __name__ == '__main__':
main()
# encoding utf-8
import requests
from lxml import etree
from urllib import request
import os
import re
def parse_page(url):
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36'
}
resp = requests.get(url, headers=headers)
text = resp.text
html = etree.HTML(text)
imgs = html.xpath("//div[@class='page-content text-center']//img[@class!='gif']")
for img in imgs:
img_url = img.get("data-original")
# get可以获取其中的元素
alt = img.get('alt')
alt = re.sub(r'[!!。\.?\?]', '', alt)
# 将文件名与后缀名分割开来,0为url
suffix = os.path.splitext(img_url)[1]
filename = alt + suffix
# 将文件下载到本地
request.urlretrieve(img_url, 'images/' + filename)
def main():
for x in range(1, 101):
url = 'https://www.doutula.com/photo/list/?page=%d' % x
parse_page(url)
break
if __name__ == '__main__':
main()
Queue线程安全队列
在线程中,访问一些全局变量,加锁是一个经常的过程。如果你是想把一些数据存储到某个队列中,那么 Python内置了一个线程安全的模块叫做 queue 模块。 Python中的queue模块中提供了同步的、线程安全以列类,包括FIFO(先进出)队列 Queue,LIFO(后入先出)队列 LifoQueue。这些队列都实现了锁原语(可以理解为原子操作,即么不做,更么都做完),能够在多程中直接使
用。可以使用队列来实现线程间的同步。相关的的数如下:
1.初始化 Queue( maxsize) 创建一个先进先出的队列。
block参数如果为True,队列满了以后进入阻塞状态
# encoding utf-8
import requests
from lxml import etree
from urllib import request
import os
import re
from queue import Queue
import threading
class Producter(threading.Thread):
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36'
}
def __init__(self, page_queue, img_queue, *args, **kwargs):
super(Producter, self).__init__(*args, **kwargs)
self.page_queue = page_queue
self.img_queue = img_queue
def run(self):
while True:
if self.page_queue.empty():
break
url = self.page_queue.get()
self.parse_page(url)
def parse_page(self, url):
resp = requests.get(url, headers=self.headers)
text = resp.text
html = etree.HTML(text)
imgs = html.xpath("//div[@class='page-content text-center']//img[@class!='gif']")
for img in imgs:
img_url = img.get("data-original")
# get可以获取其中的元素
alt = img.get('alt')
alt = re.sub(r'[!!。\.?\?\*]', '', alt)
# 将文件名与后缀名分割开来,0为url
suffix = os.path.splitext(img_url)[1]
filename = alt + suffix
self.img_queue.put((img_url, filename))
class Consumer(threading.Thread):
def __init__(self, page_queue, img_queue, *args, **kwargs):
super(Consumer, self).__init__(*args, **kwargs)
self.page_queue = page_queue
self.img_queue = img_queue
def run(self):
while True:
if self.img_queue.empty() and self.page_queue.empty():
break
img_url, filename = self.img_queue.get()
# 将文件下载到本地
request.urlretrieve(img_url, 'images/' + filename)
print(filename + " 下载完成!")
def main():
page_queue = Queue(100)
img_queue = Queue(1000)
for x in range(1, 101):
url = 'https://www.doutula.com/photo/list/?page=%d' % x
page_queue.put(url)
for x in range(5):
t = Producter(page_queue, img_queue)
t.start()
for x in range(5):
t = Consumer(page_queue, img_queue)
t.start()
if __name__ == '__main__':
main()
下载地址:http://chromedriver.storage.googleapis.com/index.html
chromedriver测试
# encoding utf-8
from selenium import webdriver
driver_path = '/Users/xxx/Downloads/chromedriver'
#选择谷歌浏览器
driver = webdriver.Chrome(executable_path=driver_path)
#打开百度
driver.get('https:www.baidu.com')
#打印源代码
print(driver.page_source)
#关闭当前页面
driver.close()
#关闭整个页面
driver.quit()
1、find_element_by_id()
2、find_element_by_name()
3、find_element_by_class_name()
4、find_element_by_xpath()
5、find_element_by_css_selector()
6、find_element_by_tag_name()
7、find_element_by_link_text()
此种方法是专门用来定位文本链接的,比如百度首页右上角有“新闻”,“hao123”,“地图”等链接
# encoding utf-8
from selenium import webdriver
from lxml import etree
driver_path = '/Users/xxx/Downloads/chromedriver'
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('https:www.baidu.com')
# 1、如果仅仅是解析网页,运用selenium+lxml方式最为快速
html = etree.HTML(driver.page_source)
# html.xpath("")
inputTag = driver.find_element_by_id('kw')
# 如果需要在输入框中赋值,需要使用send_keys
inputTag.send_keys('python')
# encoding utf-8
from selenium import webdriver
driver_path = '/Users/xxx/Downloads/chromedriver'
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('https:www.baidu.com')
inputTag = driver.find_element_by_id('kw')
# 如果需要在输入框中赋值,需要使用send_keys
inputTag.send_keys('python')
submitTag = driver.find_element_by_id('su')
# 清除输入框的内容
inputTag.clear()
# 点击百度一下按钮
submitTag.click()
from selenium import webdriver
from selenium.webdriver.support.select import Select
import time
driver_path = '/Users/xxx/Downloads/chromedriver'
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('https://www.sina.com.cn/')
#选择选择按钮
selectButton = Select(driver.find_element_by_id('slt_01'))
#通过索引点击
selectButton.select_by_index(1)
#通过地址进入
selectButton.select_by_value('地址')
#通过文字进入
selectButton.select_by_visible_text('新闻')
time.sleep(4)
driver.close()
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
driver_path = '/xxx/xxx/chromedriver'
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('https://www.baidu.com/')
inputTag = driver.find_element_by_id('kw')
submitBtn = driver.find_element_by_id('su')
#创建行为链
actions = ActionChains(driver)
#将鼠标移动到input输入框中
actions.move_to_element(inputTag)
#将input输入框输入python
actions.send_keys_to_element(inputTag, 'python')
#将鼠标移动到submi按钮上
actions.move_to_element(submitBtn)
#点击按钮
actions.click(submitBtn)
#提交行为链
actions.perform()
#cookies
from selenium import webdriver
driver_path = '/xxx/chromedriver'
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('https://www.baidu.com/')
#打印所有的cookie
for cookie in driver.get_cookies():
print(cookie)
print("=="*30)
#传递key,打印出key为PSTM的cookie信息
print(driver.get_cookie('PSTM'))
#删除key为PSTM的cookie
driver.delete_cookie('PSTM')
print(driver.get_cookie('PSTM'))
#删除所有的cookie
driver.delete_all_cookies()
1、selenium的显示等待
原理:显示等待,就是明确的要等到某个元素的出现或者是某个元素的可点击等条件,等不到,就一直等,除非在规定的时间之内都没找到,那么久跳出Exception
2、selenium的隐式等待
原理:隐式等待,就是在创建driver时,为浏览器对象创建一个等待时间,这个方法是得不到某个元素就等待一段时间,直到拿到某个元素位置。
# encoding utf-8
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver_path = '/xxx/chromedriver'
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('http://www.baidu.com')
# 隐式等待
# driver.implicity_wait(10)
# 显示等待
element = WebDriverWait(driver, 10).util(
EC.presence_of_element_located((By.ID, 'kw'))
)
element.send_keys('hello')
driver.quit()
# encoding utf-8
from selenium import webdriver
driver_path = '/xxx/chromedriver'
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('http://www.baidu.com')
# 打开新的标签页
driver.execute_script('window.open("https://www.douban.com")')
# driver切换到第二个标签页,window_handles代表句柄
driver.switch_to.window(driver.window_handles[1])
print(driver.current_url)
# encoding utf-8
from selenium import webdriver
driver_path = '/xxx/chromedriver'
# 设置代理
options = webdriver.ChromeOptions()
options.add_argument("--proxy-server=http://113.195.18.100:9999")
driver = webdriver.Chrome(executable_path=driver_path, options=options)
driver.get('http://www.baidu.com')
# encoding utf-8
from selenium import webdriver
driver_path = '/xxx/chromedriver'
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('https:www.baidu.com')
submitBtn=driver.find_element_by_id('su')
#driver继承自webelement,这个标签的某个属性
print(submitBtn.get_attribute("value"))
#保存截图
driver.save_screenshot('baidu.png')
# encoding utf-8
import requests
from lxml import etree
from lxml.html import fromstring, tostring
def request_top_url():
url = 'https://www.kugou.com/yy/rank/home/1-8888.html?from=rank'
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:77.0) Gecko/20100101 Firefox/77.0'
}
resp = requests.get(url, headers=headers)
text = resp.text
parse_detail_page(text)
def parse_detail_page(text):
html = etree.HTML(text)
ranks = html.xpath("//span[@class='pc_temp_num']//text()")
titles = html.xpath("//div[@class='pc_temp_songlist ']/ul/li")
times = html.xpath("//span[@class='pc_temp_time']/text()")
links=html.xpath("//a[@class='pc_temp_songname']/@href")
titles1=[]
for title in titles:
title=title.get('title')
titles1.append(title)
# print(titles1)
#列表生成器
ranks = [r.strip() for r in ranks if len(r.strip())]
times = [r.strip() for r in times]
# for title in titles:
# print(title)
# print(titles)
#解压缩
for rank, title, time,link in zip(ranks, titles1, times,links):
song={
'rank':rank,
'title':title,
'time':time,
'link':link
}
print(song)
# print()
def main():
request_top_url()
if __name__ == '__main__':
main()
1、创建项目
scrapy startproject [项目名称]
2、创建爬虫
scrapy genspider [爬虫名字] [爬虫域名]
注意爬虫名字跟项目名称不能冲突
spiders:存放你Spider文件,也就是你爬取的py文件
items.py:相当于一个容器,储存爬取下来的数据类型。和字典较像
middlewares.py:定义Downloader Middlewares(下载器中间件)和Spider Middlewares(蜘蛛中间件)的实现
pipelines.py:定义Item Pipeline的实现,实现数据的清洗,储存,验证。
settings.py:全局配置(请求头、多久发送一次请求、代理池)
scrapy.cfg:配置文件
pip install scrapy
即可安装。注意:
- 在
ubuntu
上安装scrapy
之前,需要先安装以下依赖:
sudo apt-get install python3-dev build-essential python3-pip libxml2-dev libxslt1-dev zlib1g-dev libffi-dev libssl-dev
,然后再通过pip install scrapy
安装。- 如果在
windows
系统下,提示这个错误ModuleNotFoundError: No module named 'win32api'
,那么使用以下命令可以解决:pip install pypiwin32
。
要使用Scrapy
框架创建项目,需要通过命令来创建。首先进入到你想把这个项目存放的目录。然后使用以下命令创建:
scrapy startproject [项目名称]
以下介绍下主要文件的作用:
items
的模型存储到本地磁盘中。scrapy gensipder qsbk "qiushibaike.com"
创建了一个名字叫做qsbk
的爬虫,并且能爬取的网页只会限制在qiushibaike.com
这个域名下。
import scrapy
class QsbkSpider(scrapy.Spider):
name = 'qsbk'
allowed_domains = ['qiushibaike.com']
start_urls = ['http://qiushibaike.com/']
def parse(self, response):
pass
其实这些代码我们完全可以自己手动去写,而不用命令。只不过是不用命令,自己写这些代码比较麻烦。
要创建一个Spider,那么必须自定义一个类,继承自scrapy.Spider
,然后在这个类中定义三个属性和一个方法。
parse
方法。这个是个固定的写法。这个方法的作用有两个,第一个是提取想要的数据。第二个是生成下一个请求的url。settings.py
代码:在做一个爬虫之前,一定要记得修改setttings.py
中的设置。两个地方是强烈建议设置的。
ROBOTSTXT_OBEY
设置为False。默认是True。即遵守机器协议,那么在爬虫的时候,scrapy首先去找robots.txt文件,如果没有找到。则直接停止爬取。DEFAULT_REQUEST_HEADERS
添加User-Agent
。这个也是告诉服务器,我这个请求是一个正常的请求,不是一个爬虫。爬虫部分代码:
import scrapy
from abcspider.items import QsbkItem
class QsbkSpider(scrapy.Spider):
name = 'qsbk'
allowed_domains = ['qiushibaike.com']
start_urls = ['https://www.qiushibaike.com/text/']
def parse(self, response):
outerbox = response.xpath("//div[@id='content-left']/div")
items = []
for box in outerbox:
author = box.xpath(".//div[contains(@class,'author')]//h2/text()").extract_first().strip()
content = box.xpath(".//div[@class='content']/span/text()").extract_first().strip()
item = QsbkItem()
item["author"] = author
item["content"] = content
items.append(item)
return items
items.py部分代码:
import scrapy
class QsbkItem(scrapy.Item):
author = scrapy.Field()
content = scrapy.Field()
pipeline部分代码:
import json
class AbcspiderPipeline(object):
def __init__(self):
self.items = []
def process_item(self, item, spider):
self.items.append(dict(item))
print("="*40)
return item
def close_spider(self,spider):
with open('qsbk.json','w',encoding='utf-8') as fp:
json.dump(self.items,fp,ensure_ascii=False)
运行scrapy项目。需要在终端,进入项目所在的路径,然后scrapy crawl [爬虫名字]
即可运行指定的爬虫。如果不想每次都在命令行中运行,那么可以把这个命令写在一个文件中。以后就在pycharm中执行运行这个文件就可以了。比如现在新创建一个文件叫做start.py
,然后在这个文件中填入以下代码:
from scrapy import cmdline
cmdline.execute("scrapy crawl qsbk".split())
1、response是一个scrapy.http.response.html.HtmlResponse
对象,可执行xpath
语法和css
语法来执行查询。
2、提取出来的数据是一个Selector
或者是一个SelectorList
对象,如果想要获取字符串,应该执行get()
或者getall()
方法。
3、getall()
方法获取Selector
中所有的文本,返回的是一个列表。
4、get()
方法获取Selector
中第一个文本,返回的是一个str类型。
5、如果数据解析回来,要传给pipline处理,那么可以用yield
来返回。或者是收集所有的item。最后统一return返回。
6、item:建议在items.py
中定义好魔性,以后就不用再使用自店。
7、pipline:这个是专门用来保存数据的。其中三个方法是会经常用的
open_spider(self, spider)
:当爬虫打开的时候执行
process_item(self, item, spider)
:当爬虫有item传过来的时候会被调用。
close_spider(self, spider)
:当爬虫关闭的时候会被调用。
要激活pipline,应该在setting.py
中,设置ITEM_PIPELINES
。示例如下:
ITEM_PIPELINES = {
'qsbk.pipelines.QsbkPipeline': 300,
}
保存json数据的时候可以使用这两个类让操作变得更简单
1、JsonItemExporter
:这个是每次把数据添加到内存中,最后统一写入磁盘中。好处是存储的数据是一个满足json规则的数据,坏处是如果数据量比较大,会比较耗内存,示例代码如下
from scrapy.exporters import JsonItemExporter
class QsbkPipeline:
def __init__(self):
self.f = open('duanzi.json', 'wb')
self.exporter = JsonItemExporter(self.f, ensure_ascii=False, encoding='utf-8')
self.exporter.start_exporting()
def open_spider(self, spider):
print('爬虫开始。。')
def process_item(self, item, spider):
self.exporter.export_item(item)
return item
def close_spider(self, spider):
self.exporter.finish_exporting()
self.f.close()
print('爬虫结束了。。。')
2、JsonLinesItemExporter
:这个是每次调用export_item
的时候就把这个item存储到硬盘中。坏处是每一个字典一行,整个文件不是一个满足json格式的文件。好处是每次处理数据的时候就直接存储到了硬盘中,这样不会耗内存,数据也比较安全。示例代码如下
from scrapy.exporters import JsonLinesItemExporter
class QsbkPipeline:
def __init__(self):
self.f = open('duanzi.json', 'wb')
self.exporter = JsonLinesItemExporter(self.f, ensure_ascii=False, encoding='utf-8')
def open_spider(self, spider):
print('爬虫开始。。')
def process_item(self, item, spider):
self.exporter.export_item(item)
return item
def close_spider(self, spider):
self.f.close()
print('爬虫结束了。。。')
在上一个糗事百科的爬虫案例中。我们是自己在解析完整个页面后获取下一页的url,然后重新发送一个请求。有时候我们想要这样做,只要满足某个条件的url,都给我进行爬取。那么这时候我们就可以通过CrawlSpider
来帮我们完成了。CrawlSpider
继承自Spider
,只不过是在之前的基础之上增加了新的功能,可以定义爬取的url的规则,以后scrapy碰到满足条件的url都进行爬取,而不用手动的yield Request
。
之前创建爬虫的方式是通过scrapy genspider [爬虫名字] [域名]
的方式创建的。如果想要创建CrawlSpider
爬虫,那么应该通过以下命令创建:
scrapy genspider -t crawl [爬虫名字] [域名]
使用LinkExtractors
可以不用程序员自己提取想要的url,然后发送请求。这些工作都可以交给LinkExtractors
,他会在所有爬的页面中找到满足规则的url
,实现自动的爬取。以下对LinkExtractors
类做一个简单的介绍:
class scrapy.linkextractors.LinkExtractor(
allow = (),
deny = (),
allow_domains = (),
deny_domains = (),
deny_extensions = None,
restrict_xpaths = (),
tags = ('a','area'),
attrs = ('href'),
canonicalize = True,
unique = True,
process_value = None
)
主要参数讲解:
定义爬虫的规则类。以下对这个类做一个简单的介绍:
class scrapy.spiders.Rule(
link_extractor,
callback = None,
cb_kwargs = None,
follow = None,
process_links = None,
process_request = None
)
主要参数讲解:
LinkExtractor
对象,用于定义爬取规则。CrawlSpider
使用了parse
作为回调函数,因此不要覆盖parse
作为回调函数自己的回调函数。主程序代码如下
# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from ..items import WxappItem
class WxappApiderSpider(CrawlSpider):
name = 'wxapp_apider'
allowed_domains = ['wxapp-union.com']
start_urls = ['http://www.wxapp-union.com/portal.php?mod=list&catid=2&page=1']
rules = (
Rule(LinkExtractor(allow=r'.+mod=list&catid=2&page=\d'), follow=True),
Rule(LinkExtractor(allow=r'.+article-.+\.html'), callback='parse_item', follow=True),
)
def parse_item(self, response):
title=response.xpath("//h1[@class='ph']/text()").get()
authors_p=response.xpath("//p[@class='authors']")
author=authors_p.xpath(".//a/text()").get()
pub_time=authors_p.xpath(".//span/text()").get()
content=response.xpath("//td[@id='article_content']//text()").getall()
content="".join(content)
item=WxappItem(title=title,author=author,pub_time=pub_time,content=content)
yield item
1、其中LinkExtractor
中的allow
中填写允许的域名,其中填写正则表达式,满足正则表达式的都会被提取。
2、什么情况下使用follow,如果在爬取页面的时候,需要将满足条件的url再进行跟进,设置为True,否则设置为False。
3、什么情况下使用callback:如果这个url对应的页面,只是为了获取更多的url,并不需要里面具体的数据,那么可以不指定callback。
1、可以方便的做一些数据提取的测试代码。
2、如果想要执行scrapy命令,要先进入到scrapy所在的环境中。
3、如果想要夺取某个项目的配置信息,那么应该先禁图到这个项目中,再执行scrapy shell
命令。
scrapy shell 'url地址'
1、想要发送post请求,那么推荐使用scrapy FormRequest0
方法,可以方便的指定表单数据。
2、如果想在爬虫一开始的时候就发送post请求,吗么应该重写start_requests
方法,在这个方法中发送post请求,代码如下:
import scrapy
class RenrenSpider(scrapy.Spider):
name = 'renren'
allowed_domains = ['renren.com']
start_urls = ['http://renren.com/']
#此处为重写start_requests方法,发送post请求登陆
def start_requests(self):
url='http://www.renren.com/PLogin.do'
data = {
'email': '手机号',
'password': '密码'
}
request=scrapy.FormRequest(url=url,formdata=data,callback=self.parse_page)
yield request
def parse_page(self,response):
request=scrapy.Request(url='http://www.renren.com/id/profile',callback=self.parse_profile)
yield request
def parse_profile(self,response):
with open('dapeng.html','w',encoding='utf-8') as f:
f.write(response.text)
当使用Files Pipeline
下载文件的时候,按照以下步骤来完成:
Items
,然后在这个 item
中定义两个属性,分别为 file_urls
以及 files
。file_urls
是用来存储需要下载的图片的url链接,需要给一个列表。item
的files
属性中。比如下载路径、下载的url和文件的校验码等。settings.py
中配置 FILES_STORE
,这个配置是用来设置文件下载下来的路径。pipeline
:在 ITEM_PIPELINES
中设置 scrapy.pipelines.files.FilesPipeline:1
。当使用Image Pipeline
下载文件的时候,按照以下步骤来完成:
image_urls
以及 images
。image_urls
是用来存储需要下载的图片的url链接,需要给一个列表。item
的images
属性中。比如下载路径、下载的url和文件的校验码等。settings.py
中配置 IMAGES_STORE
,这个配置是用来设置文件下载下来的路径。pipeline
:在 ITEM_PIPELINES
中设置 scrapy.pipelines.images.ImagesPipeline:1
。srcs = list(map(lambda x: response.urljoin(x), srcs))
map(function, iterable, ...)
map(lambda x: x ** 2, [1, 2, 3, 4, 5]) # 使用 lambda 匿名函数
[1, 4, 9, 16, 25]
http://www.useragentstring.com
1、Middleware.py文件如下设置
class UserAgentDownloadMiddleware(object):
USER_AGENTS = ['Mozilla/5.0 (compatible; MSIE 9.0; AOL 9.0; Windows NT 6.0; Trident/5.0)',
'Mozilla/5.0 (compatible; MSIE 9.0; AOL 9.1; AOLBuild 4334.5012; Windows NT 6.0; WOW64; Trident/5.0)',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:78.0) Gecko/20100101 Firefox/78.0',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36',
'Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.9a3) Gecko/20070409 BonEcho/2.0.0.3',
'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:77.0) Gecko/20100101 Firefox/77.0']
def process_request(self, request, spider):
user_agent = random.choice(self.USER_AGENTS)
request.headers['User-Agent'] = user_agent
注:USER_AGENTS必须为列表,才能使用random.choice
进行选择
2、爬虫文件如下配置
def parse(self, response):
user_agent = json.loads(response.text)['user-agent']
print(user_agent)
yield scrapy.Request(self.start_urls[0], dont_filter=True)
其中yield
为一直发送请求
dont_filter=True
为设置不删除重复的链接,即可以一直访问相同的链接。
设置代理方法:
request.mate['proxy']=proxy
代码如下:
from twisted.enterprise import adbapi
from pymysql import cursors
class JianshuTwistedPipeline(object):
def __init__(self):
dbparms = {
'host': '127.0.0.1',
'user': 'root',
'password': 'root',
'database': 'jianshu',
'port': 3306,
'charset': 'utf8',
# 此处必须填写cursor类,否则回直接使用默认cursor类
'cursorclass': cursors.DictCursor
}
self.dbpool = adbapi.ConnectionPool('pymysql', **dbparms)
self._sql = None
@property
def sql(self):
if not self._sql:
self._sql = """
insert into article(id,title,content,origin_url,article_id) values (null ,%s,%s,%s,%s)
"""
return self._sql
return self._sql
def process_item(self, item, spider):
# 此处为添加item,将调价item给self.insert_item函数处理
defer = self.dbpool.runInteraction(self.insert_item, item)
defer.addErrback(self.handle_error, item, spider)
return item
# 定义插入数据库代码
def insert_item(self, cursor, item):
cursor.execute(self.sql, (item['title'], item['content'], item['origin_url'], item['article_id']))
# 处理错误代码
def handle_error(self, error, item, spider):
print('=' * 10 + 'error' + '=' * 10)
print(error)
print('=' * 10 + 'error' + '=' * 10)