Python爬虫
如果你想查看python的版本 python -
问题一:# Unverified HTTPS request is being made to host.
在爬虫的时候出现一些报错但是并不影响其效果。
我们将其警告隐藏即可。
错误提示:
Unverified HTTPS request is being made to host '爬取的网址'. Adding certificate verification is strongly advised. See: urllib3.readthedocs.io/en/latest/a…
解决方式:
#在最顶部加上
requests.packages.urllib3.disable_warnings()
问题二:如何解决网站防盗链的问题呢?
网站加入防盗链的目的就是为了防止恶意爬虫,它的原理就是追根溯源九链接地址,追问当前请求地址的上一级是谁,不能无缘无故的断开
其实解决的方法也很简单,就是url的请求头中加入Referer
headers={
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36",
"Referer": "https://www.pearvideo.com/video_1761246"
}
问题三:抓取大量数据时,运用到多线程(或多进程)
Python 使用def 开始函数定义,紧接着是函数名,括号内部为函数的参数,内部为函数的 具体功能实现代码,如果想要函数有返回值。
在Python,我们经常会编写
if__name__ ``=``='__main__'这么一段代码,这段代码该怎么来理解? 这段代码的功能理解如下: 一个python的文件有两种使用的方法:
- 作用一,直接作为脚本执行。
- 作用二,import到其他的python脚本中被调用(模块重用)执行。
if __name__ == '__main__': 的作用就是控制这两种情况执行代码的过程,在if __name__ == '__main__': 下的代码只有在第一种情况下(即文件作为脚本直接执行)才会被执行,而import到其他脚本中是不会被执行的。
# 1.如何提取单个页面的数据
# 2.上线程池,多个页面同时抓取
#解决当爬取数据多的时候,可以分多线程快速提取数据
from concurrent.futures import ThreadPoolExecutor
import requests
from lxml import etree
import csv
f=open("Part4/菜价.csv",mode="w",encoding="utf-8")
csvwriter=csv.writer(f)
def download_one_page(url):
# 拿到页面源代码
resp=requests.get(url)
html=etree.HTML(resp.text)
table=html.xpath("/html/body/div[3]/div[1]/div[1]/table")[0]
# trs=table.xpath("./tr[position()>1]")
trs=table.xpath("./tbody/tr")
#拿到每个tr
for tr in trs:
txt=tr.xpath("./td/a/text()")[0]
print(txt)
#把数据存放在文件中
csvwriter.writerow(txt)
print(url,"提取完成!")
# python中_name_是一个系统变量 如果当前模块为主模块,那么此模块的名称为'__main__'(注意这个下划线是长的)
if __name__=='__main__':
with ThreadPoolExecutor(50) as t:
for i in range(1,25):
t.submit(download_one_page,f"https://xianhuo.cngold.org/hq_farm_p{i}.htm")
问题四:线程和进程的到底是什么呢?呢?
一、进程和线程概念
进程:进程是一个很抽象的概念,指在系统中能独立运行并作为资源分配的基本单位,它是由一组机器指令、数据和堆栈等组成的,是一个能独立运行的活动实体。具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。进程简单来理解就是每个应用都是一个进程。
线程:是用来执行具体功能和任务的,需要进程为载体,是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。是并发执行的程序在执行过程中分配和管理资源的基本单位,是一个动态概念,竟争计算机系统资源的基本单位。
注:一个程序至少有一个进程,一个进程至少有一个线程(主线程),进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
二、进程和线程的区别
操作系统资源管理方式是不一样的,进程有独立的地址空间,进程崩溃后会有保护模式让其不会对其他的进程产生影响。而线程则不然,线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,所以一个线程挂掉可能影响整个进程挂掉。 进程的并发性没有线程高。 每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中。由应用程序提供多个线程执行控制。 对于应用程序来说,多线程是可以同时有多个执行部分同时执行。但对于操作系统来说是没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配 注:多线程容易调度,有效地实现并发性。对内存的开销比较小。创建线程比创建进程要快。
三、线程的状态
创建:new了但是没有启动的线程的状态。比如"Thread t = new Thread()",t就是一个处于NEW状态的线程 运行:创建完成之后,需要调用start()方法让线程在Java虚拟机中运行,让线程达到running状态。在虚拟机中可能是正在执行任务,也有可能是正在等待处理器的资源,线程必须获得CPU的资源才能运行run()方法。 阻塞:有一个线程正在等待监视器锁,这个代表这个线程的进入blocked阻塞状态。 等待:如果线程调用了 <不超时> wait()方法,join()方法,LockSupport的park()方法以后就会让线程处于一个waiting等待状态。 等待超时:如果线程调用了指定正等待时间的Object的wait()方法、Thread的join()方法、Thread的sleep()方法、LockSupport的parkNanos()方法、LockSupport的parkUntil()方法,就会处于timed_waiting超时等待状态了。 停止:线程调用终止或者run()方法执行结束后,线程就会处于terminated终止状态。 注:线程运行图
问题五:什么是协程
一、什么是协程
协程,英文Coroutines,是一种比线程更加轻量级的存在。
协程不是进程,也不是线程,它就是一个可以在某个地方挂起的特殊函数,并且可以重新在挂起处继续运行。所以说,协程与进程、线程相比,不是一个维度的概念。
一个进程可以包含多个线程,一个线程也可以包含多个协程,也就是说,一个线程内可以有多个那样的特殊函数在运行。但是有一点,必须明确,一个线程内的多个协程的运行是串行的。如果有多核CPU的话,多个进程或一个进程内的多个线程是可以并行运行的,但是一个线程内的多个协程却绝对串行的,无论有多少个CPU(核)。这个比较好理解,毕竟协程虽然是一个特殊的函数,但仍然是一个函数。一个线程内可以运行多个函数,但是这些函数都是串行运行的。当一个协程运行时,其他协程必须挂起。
二、协程、线程、进程关于上下文切换的比较
表面上,进程、线程、协程都存在上下文切换的问题,但是三者上下文切换又有明显不同,见下表:
三、协程的使用场景
一个线程内的多个协程是串行执行的,不能利用多核,所以,显然,协程不适合计算密集型的场景。协程适合I/O 阻塞型。
I/O本身就是阻塞型的(相较于CPU的时间世界而言)。就目前而言,无论I/O的速度多快,也比不上CPU的速度,所以一个I/O相关的程序,当其在进行I/O操作时候,CPU实际上是空闲的。
我们假设这样的场景,如下图:1个线程有5个I/O的事情(子程序)要处理。如果我们绝对的串行化,那么当其中一个I/O阻塞时,其他4个I/O并不能得到执行,因为程序是绝对串行的,5个I/O必须一个一个排队等待处理,当一个I/O阻塞时,其它4个也得等着。
而协程能比较好地处理这个问题,当一个协程(特殊子进程)阻塞时,它可以切换到其他没有阻塞的协程上去继续执行,这样就能得到比较高的效率,如下图所示:
上面举的例子是5个I/O处理,如果每秒500个,5万个或500万个呢?已经达到了“I/O密集型”的程度,而“I/O密集型”确实是协程无法应付的,因为它没有利用多核的能力。这个时候的解决方案就是“多进程+协程”了。
所以说,I/O阻塞时,利用协程来处理确实有优点(切换效率比较高),但是我们也需要看到其不能利用多核的这个缺点,必要的时候,还需要使用综合方案:多线程+协程。