html全称HyperText Mackeup Language,翻译为超文本标记语言,它不是一种编程语言,是一种描述性的标记语言,用于描述超文本内容的显示方式,主要由三样东西构成:
对于初学者来说,要想写一个前端的页面,最先要了解前端页面源代码的大体结构的构成,然后再往里面镶嵌自己想要增加的内容
中小企业一般情况下,数据都是短板,只能靠爬虫去采集数据
舆情监控
竞品分析、了解行情
要想通过Python从浏览器中爬取数据,首先要在python中导入一个三方库requests,利用requests的属性来获取网页中的数据信息
URI - Universal Resource Identifier - 统一资源标识符
URL - Universal Resource Locator - 统一资源定位符
URI = URL + URN(x)
协议 - HTTP / HTTPS
DNS - Domain Name System - 将域名解析为IP地址
HTTP请求
首先在Python中导入三方库:import requests
get请求:可以请求任何网站的URL
r = requests.get('https://api.github.com/events')
post请求
r = requests.post('http://httpbin.org/post', data = {
'key':'value'})
法不禁止即为许可。爬虫是一个灰色地带,总的来说是不违法的,但是我们在进行爬虫时也要注意隐匿自己的身份,爬虫协议:robots.txt
只需要传递一个dict给headers参数就可以了,headers参数里面是一个字典,添加’User-Agent’的键,再把网页源代码的User-Agent的值复制粘贴过来就可以了
import re
import requests
resp = requests.get(
url='https://movie.douban.com/top250',
headers={
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36'
}
)
# 418 - I am a teapot.
print(resp.status_code)
print(resp.text)
要了解我们爬取到的是页面的全部信息,但是我们可能只是需要其中的一部分信息,其他繁杂无用的信息都不需要,因此我们要对我们爬取到的页面信息进行解析,从而获取到自己想要的那一部分
import re
import requests
resp = requests.get(
url='https://movie.douban.com/top250',
headers={
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36'
}
)
# 使用正则表达式捕获组从页面代码中提取电影名称
pattern = re.compile(r'\(.*?)\<\/span\>')
print(type(pattern))
print(pattern.findall(resp.text))
虽然使用正则表达式性能较好,但是由于正则表达式使用起来较难,因此我们更加常用的解析方式是CSS选择器解析,使用起来较为简单
Beautiful Soup 是一个可以从HTML或XML文件中提取数据的Python库.它能够通过你喜欢的转换器实现惯用的文档导航,查找,修改文档的方式
import bs4
resp = requests.get('https://www.sohu.com/index.html')
if resp.status_code == 200:
soup = bs4.BeautifulSoup(resp.text, 'html.parser')
resp = requests.get('https://www.sohu.com/index.html')
if resp.status_code == 200:
soup = bs4.BeautifulSoup(resp.text, 'html.parser')
anchors = soup.select('div.list16>ul>li>a')
for anchor in anchors:
# 通过标签对象的attrs属性的索引操作获取指定的属性值
print(anchor.attrs['title'])
网上有很多专业的付费商业代理,一般在我们购买之后,都是提供给我们一个网络API接口(URL),通过请求这个接口就可以获得代理的信息。
让代理服务器帮我们向目标网站发起请求,目标网站返回的响应由代理返回给我们
如果做商业爬虫项目一般情况都需要购买商业IP代理(大量的代理服务器,失效了随时更换)
以蘑菇代理举例:
resp = requests.get('http://piping.mogumiao.com/proxy/api/get_ip_bs?appKey=d36d8b6f1703481eb6c07cc78b3be0c1&count=5&expiryDate=0&format=1&newLine=2')
# json是将返回的键值对变成字典,再放到列表里面,拿到代理服务器的列表
proxy_list = resp.json()['msg']
for page in range(10):
# 随机抽出来一个代理服务器字典
proxy_dict = random.choice(proxy_list)
ip, port = proxy_dict['ip'], proxy_dict['port']
try:
resp = requests.get(
url=f'https://movie.douban.com/top250?start={page*25}',
headers={
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36'
},
# 设置代理
proxies={
# 'http': f'http://{ip}:{port}',
'https': f'http://{ip}:{port}' # http还是https,要和URL保持一致
},
timeout=3,
verify=False
)
resp = requests.get(
url=f'https://movie.douban.com/top250?start={page*25}',
# 商业爬虫项目,需要提前创建好一个cookies池,每次请求从池子中随机选择一组cookies池
headers={
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36',
'Cookie': 'll="118318"; bid=hXdgbkzPeJk; __utmz=30149280.1609834076.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); __utmz=223695111.1609834112.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); __gads=ID=f2030357b13e661c-22c60d118dc500e4:T=1609840601:RT=1609840601:S=ALNI_MZx7S3mwNu_4fmaxmLsq20IlV3-0g; _vwo_uuid_v2=D579DA2B17F60E6D942734D1F3D975CCE|a4ed238b8f9fb09cd5f70bdb39a4e433; __yadk_uid=v1F1uNJZRDv3uEbSShXyw7NSqp36GVTd; dbcl2="229808144:LsAFGkwzIc8"; push_noty_num=0; push_doumail_num=0; __utmv=30149280.22980; douban-profile-remind=1; ck=Igw5; __utmc=30149280; __utmc=223695111; ap_v=0,6.0; __utma=30149280.171995204.1609834076.1609901169.1609916179.6; __utmb=30149280.0.10.1609916179; __utma=223695111.1649735111.1609834112.1609901169.1609916179.6; __utmb=223695111.0.10.1609916179; _pk_ses.100001.4cf6=*; _pk_id.100001.4cf6=f6918bb273f7004a.1609834112.6.1609917103.1609901181.'
}
)
在我们获得爬取到的数据时,要将他们放在Excel表格里面,这样方便后续的保存和查看
要想保存在Excel表格里面,首先要利用一个三方库:xlwt,在终端里把它安装上
然后需要创建一个工作簿对象: wb = xlwt.Workbook( )
再创建工作表:sheet = wb.add_sheet(‘工作报表的名称’)
指定行索引、列索引,往表里写入数据
最后保存Excel表:wb.save(‘保存的名称’)
wb = xlwt.Workbook() # 创建一个工作簿对象
sheet = wb.add_sheet('TOP250') # 创建一个表单
col_names = ('排名', '名称', '评分', '类型', '制片国家', '语言', '时长') # 创建Excel表格表头
for index, name in enumerate(col_names):
sheet.write(0, index, name)
首先要了解csv文件是一个文本文件
import csv
with open('test.csv', 'w', encoding='utf-8') as file:
csv_writer = csv.writer(file, quoting=csv.QUOTE_ALL)
csv_writer.writerow(['张飞', 90, 80, 70])
csv_writer.writerow(['关羽', 98, 80, 70])
csv_writer.writerow(['马超', 95, 85, 70])
csv_writer.writerow(['赵云', 34, 80, 70])
csv_writer.writerow(['黄忠', 67, 56, 70])
import csv
from Tools.scripts.mkreal import join
with open('2018年北京积分落户数据.csv', 'r', encoding='utf-8') as file:
csv_reader = csv.reader(file)
for row in csv_reader:
print(',', join(str(row)))
# print(f'编号:{row[0]}')
# print(f'编号:{row[1]}')
# print(f'编号:{row[2]}')
# print(f'编号:{row[3]}')
# print(f'编号:{row[4]}')
CPython ----> C -----> GIL(全局解释器锁)
Jython ------> Java
IronPython ----> C#
PyPy -----> Python ----> JIT
页面上可能有动态内容(通过JavaScript代码动态生成,显示网页源代码时看不到内容)
方法一:JavaScript逆向 —> 找真正提供数据的URL
方法二:通过Python代码操控浏览器,直接拿到带动态内容的页面,然后再提取数据+ +
selenium.webdriver
pip install selenium
浏览器一般有:Chrome / Firefox / Safari / Edge / Opera
from selenium import webdriver
import bs4
driver = webdriver.Chrome()
driver.get('https://image.so.com/z?ch=beauty')
# page_source是带动态内容的页面源代码
soup = bs4.BeautifulSoup(driver.page_source, 'html.parser')
imgs = soup.select('img')
for img in imgs:
print(img.attrs['src'])
进程:我们运行的程序通常会对应到一个或多个进程,进程是操作系统分配内存的基本单位
线程:一个进程通常会包含一个或多个线程,线程是操作系统分配CPU的基本单位
单线程程序 —> 我们的程序中只有一个执行线索(主线程)—> 创建多个线程(有多个可以并发的部分)
并发编程:让程序有多个执行线索(有多个部分能够齐头并进的执行)
线程池:先用一个容器,提前创建好若干个线程放进去,用线程的时候从线程池中借出一个线程,用完了之后,不要释放线程,而是把这个线程放回池子,让线程可以被重复利用
池化技术基本上都是空间换时间的做法
当程序中有耗时间的任务,可能会阻塞别的任务的执行,并且他们之间没有因果关系,就可以利用多线程来同时进行
def main( ):
t1 = Thread(target = output1)
import random
import time
def record_time(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f'{func.__name__}执行时间:{end-start:.3f}秒')
return result
return wrapper
@record_time
def download(filename):
print(f'开始下载{filename}')
time.sleep(random.randint(5, 10))
print(f'{filename}下载完成')
@record_time
def main():
print(download.__name__)
t1 = Thread(target=download, args=('窗前明月光.pdf',))
t1.start()
t2 = Thread(target=download, args=('疑是地上霜.avi',))
t2.start()
t3 = Thread(target=download, args=('原来是幻觉.avi',))
t3.start()
t1.join()
t2.join()
t3.join()
print(f'下载总时间: ')
if __name__ == '__main__':
main()
tasks = threadpool.makeRequests(download, [f'电影{x}' for x in range(100)])
tasks2 = threadpool.makeRequests(save, [0])
pool = threadpool.ThreadPool(10)
for task in tasks2:
pool.putRequest(task)
for task in tasks:
pool.putRequest(task)
第四步:执行和等待
pool.wait()
当有多个线程想要竞争一个资源,想要保护资源(在关键操作上只有一个线程能够访问到这个资源)就需要用到线程锁,当一个线程拿到锁的时候,其他线程只能等到该线程执行完之后释放锁,再进行争抢锁
当一个线程拿到锁之后,可能会死在里面,但是其他线程又没有接收到释放锁的消息,无法拿到锁,这就会造成死锁的情况,这种情况就可以利用线程调度来解决。