Python爬虫教程问题汇总

120 阅读8分钟

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终止状态。 注:线程运行图

image.png

问题五:什么是协程

一、什么是协程

协程,英文Coroutines,是一种比线程更加轻量级的存在。

协程不是进程,也不是线程,它就是一个可以在某个地方挂起的特殊函数,并且可以重新在挂起处继续运行。所以说,协程与进程、线程相比,不是一个维度的概念。

一个进程可以包含多个线程,一个线程也可以包含多个协程,也就是说,一个线程内可以有多个那样的特殊函数在运行。但是有一点,必须明确,一个线程内的多个协程的运行是串行的。如果有多核CPU的话,多个进程或一个进程内的多个线程是可以并行运行的,但是一个线程内的多个协程却绝对串行的,无论有多少个CPU(核)。这个比较好理解,毕竟协程虽然是一个特殊的函数,但仍然是一个函数。一个线程内可以运行多个函数,但是这些函数都是串行运行的。当一个协程运行时,其他协程必须挂起。

image.png

二、协程、线程、进程关于上下文切换的比较

表面上,进程、线程、协程都存在上下文切换的问题,但是三者上下文切换又有明显不同,见下表:

image.png

三、协程的使用场景

一个线程内的多个协程是串行执行的,不能利用多核,所以,显然,协程不适合计算密集型的场景。协程适合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个也得等着。

image.png

而协程能比较好地处理这个问题,当一个协程(特殊子进程)阻塞时,它可以切换到其他没有阻塞的协程上去继续执行,这样就能得到比较高的效率,如下图所示:

image.png

上面举的例子是5个I/O处理,如果每秒500个,5万个或500万个呢?已经达到了“I/O密集型”的程度,而“I/O密集型”确实是协程无法应付的,因为它没有利用多核的能力。这个时候的解决方案就是“多进程+协程”了。

所以说,I/O阻塞时,利用协程来处理确实有优点(切换效率比较高),但是我们也需要看到其不能利用多核的这个缺点,必要的时候,还需要使用综合方案:多线程+协程。