爬虫笔记

学一下爬虫基础,做一下笔记


准备材料:
python3基础
fiddler
chrome

Urllib

urlopen & request

从request模块中导入urlopen,可以直接使用这个函数来解析一个url,然后返回一个HTTPResponse对象。
可以使用read来读出然后进行解码。

from urllib.request import urlopen

responds = urlopen('http://www.baidu.com')
html = responds.read()
print(html.decode())

同时可以使用info来获取服务响应的HTTP报头,使用getcode来获取响应码,使用geturl来获取实际的url可以防止重定向

responds = urlopen('http://www.baidu.com')
print(responds.info())
print(responds.getcode())
print(responds.geturl())

urlopen还可以传入一个Request对象,可以使用Request对象来包装请求(关于headers里属性的值可以看看这个大佬的文这里)。
使用headers参数可以添加请求头, 其形式是一个字典的形式,同时也可以使用get_header然后传入属性的形式查看header里属性的值。

from urllib.request import urlopen, Request

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36",
    "Accept-Language": "zh-CN,zh;q=0.9,zh-TW;q=0.8,en-US;q=0.7,en;q=0.6"
}

request = Request('http://www.baidu.com', headers=headers)
print(request.get_header('Accept-language'))
responds = urlopen(request)

关于设置User-Agent是为了防止反爬虫,可以安装fake _useragent来获取更多的User-Agent的值。
从fake_useragent 里导入UserAgent,然后通过.浏览器名的形式,每次都随机的获取一个useragent

from urllib.request import urlopen, Request
from fake_useragent import UserAgent

ua = UserAgent()

headers = {
    "User-Agent": ua.chrome,
    "Accept-Language": "zh-CN,zh;q=0.9,zh-TW;q=0.8,en-US;q=0.7,en;q=0.6"
}

request = Request('http://www.baidu.com', headers=headers)
responds = urlopen(request)

GET

假设在bing中搜索"你好世界",就会得到这么一个URL

https://www.bing.com/search?q=你好世界&PC=U316&FORM=CHROMN

这个get请求是以key=value的形式发送即这里q=的值是我们搜索的关键字,但是实际发送请求时需要进行编码,可以在fiddler里查看,实际的get请求是长这样的

/search?q=%E4%BD%A0%E5%A5%BD%E4%B8%96%E7%95%8C&PC=U316&FORM=CHROMN

可以发现汉字被进行了编码,所以实际上我们在爬虫中发送请求时也要这么做才行。
要这么做实际上可以直接用parse中的quote进行编码

from urllib.request import urlopen, Request
from fake_useragent import UserAgent
from urllib.parse import quote

ua = UserAgent()

headers = {
    "User-Agent": ua.chrome,
    "Accept-Language": "zh-CN,zh;q=0.9,zh-TW;q=0.8,en-US;q=0.7,en;q=0.6"
}

url = "https://www.bing.com/search?q={}&PC=U316&FORM=CHROMN".format(quote("你好世界"))
request = Request(url, headers=headers)
responds = urlopen(request)

也可以是urlencode进行编码,这样更方便。

from urllib.request import urlopen, Request
from fake_useragent import UserAgent
from urllib.parse import quote
from urllib.parse import urlencode # 导入包
args = { # 参数,可以写多个
    "q": '你好世界',
    'PC': 'U316',
    'FORM': 'CHROMN'
}

url = "https://www.bing.com/search?{}".format(urlencode(args))
print(url)
"""
https://www.bing.com/search?q=%E4%BD%A0%E5%A5%BD%E4%B8%96%E7%95%8C&PC=U316&FORM=CHROMN
"""

POST

除了get请求还有post请求,比如登录时就需要发送post请求,比如vjugde的登录。首先可以先登录一下,同时打开审查元素。然后查找post请求。
在这里插入图片描述
然后就可以找到发送post的url,向下翻可以找到发送的数据
在这里插入图片描述
知道了这些就可以发送POST请求了
只需要在Request类中加上data这一项即可。

from urllib.request import urlopen, Request
from fake_useragent import UserAgent
from urllib.parse import quote
from urllib.parse import urlencode

ua = UserAgent()

headers = {
    "User-Agent": ua.chrome,
    "Accept-Language": "zh-CN,zh;q=0.9,zh-TW;q=0.8,en-US;q=0.7,en;q=0.6"
}

form_data = { # form data,需要使用urlencode后然后encode转成bytes类型。
    'username': 'zipper112',
    'password': '*********'
}

request1 = Request('https://vjudge.net/user/login', data=urlencode(form_data).encode(), headers=headers)
response1 = urlopen(request1)
ajax

关于ajax可以看这个大佬篇文章这里
我的理解就是一点一点加载网页内容的技术。
比如豆瓣的这个界面,向下滑时就会一点一点加载出电影豆瓣
然后打开审查元素选择XHR类型,一点一点向下滑动,就会发现有新加载出的请求
在这里插入图片描述
然后分析这些请求找出规律,就可以进行爬取。

关于https请求,有时会因为证书原因报错,此时可以直接不验证证书。

from urllib.request import urlopen, Request
from fake_useragent import UserAgent
from urllib.parse import quote
from urllib.parse import urlencode
import ssl # 导入ssl

ua = UserAgent().chrome

headers_1 = {
    "User-Agent": ua,
}

request = Request('https://www.baidu.com', headers=headers_1)
response = urlopen(request, context=ssl._create_unverified_context()) # 添加上下文,不验证证书
content = response.read().decode()
print(content)


proxy &cookie

有时被反爬虫封ip就很无奈,此时就需要挂代理通过代理ip来继续进行爬取。
查看urlopen的源码,可以发现,其实urlopen返回的是opener.open(url, data, timeout),其中opener = build_opener(https_handler)。我们可以从这里添加代理。

from urllib.request import urlopen, Request, build_opener, HTTPHandler
from fake_useragent import UserAgent
from urllib.parse import urlencode
import ssl

headers_1 = {
    "User-Agent": UserAgent().chrome
}

request = Request('https://www.baidu.com', headers=headers_1)

handler = HTTPHandler() # 用于处理http请求的handler
opener = build_opener(handler) # 用build_opener来创建对应的opener
respone = opener.open(request) # 使用open的方法发送请求
print(respone.read().decode())

然后使用ProxyHandler来进行代理

from urllib.request import urlopen, Request, build_opener, ProxyHandler
from fake_useragent import UserAgent
from urllib.parse import urlencode
import ssl

headers_1 = {
    "User-Agent": UserAgent().chrome
}

request = Request('https://www.baidu.com', headers=headers_1)

handler = ProxyHandler({"https": "144.217.101.245:3129"}) # 代理方式: ip:端口
opener = build_opener(handler)
respone = opener.open(request)

想使用cookie可以直接再header里添加cookie,只需要在对应页面f12然后找到对应的文件复制粘贴即可。

from urllib.request import urlopen, Request, build_opener, HTTPHandler
from fake_useragent import UserAgent
from urllib.parse import urlencode
import ssl

headers_1 = {
    "User-Agent": UserAgent().chrome,
    "Cookie": "_ga=GA1.2.208854112.1596096173; __gads=ID=875e898bfff05764-5970ea8c400dd:T=1604833659:RT=1604833659:S=ALNI_MaYV1KJxYwPigDE2RLXmosyAcPdxQ; _gid=GA1.2.1582206481.1609920671; JSESSIONID=9470354D57EE958059A83AA4A838BB2F; Jax.Q=zipper112|BJWUT7IM9R0LL2LCDZY43RGOAGYULD; _gat_gtag_UA_13231844_2=1"
}

request = Request('https://vjudge.net/contest/417157', headers=headers_1)

handler = HTTPHandler()
opener = build_opener(handler)
respone = opener.open(request)
print(respone.read().decode())

但是HTTPhandler不会保存cookie,也就是使用Httphadler post之后不会保存状态。想要保存就得使用HTTPCookieProcessor

from urllib.request import urlopen, Request, build_opener, HTTPCookieProcessor
from fake_useragent import UserAgent
from urllib.parse import urlencode
import ssl

ua = UserAgent().chrome
headers = {
    "User-Agent": ua,
 }

args = {
    "username": "zipper112",
    "password": "123456789"
}

request_1 = Request('https://vjudge.net/user/login', headers=headers, data=urlencode(args).encode()) # 用于登录
request_2 = Request('https://vjudge.net/contest/417157', headers=headers) # 登录之后发送Get请求

handler = HTTPCookieProcessor()
opener = build_opener(handler)
respone_1 = opener.open(request_1) # 先登录
respone_2 = opener.open(request_2) # 后GET请求
print(respone_2.read().decode())

HttpCookieProcessor支持传入一个参数,这个参数用于保存cookie,如果不传入就会自动创建。可以传入FileCookieJar, MozillaCookieJar, LWPCookieJar他们都是CookieJar下面的类。
使用MozillaCookieJar可以实现Cookie的文件保存与读取。

from urllib.request import urlopen, Request, build_opener, HTTPCookieProcessor
from fake_useragent import UserAgent
from urllib.parse import urlencode
from http.cookiejar import MozillaCookieJar # 导入MozillaCookieJar
import ssl

ua = UserAgent().chrome
headers = {
    "User-Agent": ua,
 }

args = {
    "username": "zipper112",
    "password": "123456789"
}

request_1 = Request('https://vjudge.net/user/login', headers=headers, data=urlencode(args).encode())
request_2 = Request('https://vjudge.net/contest/417157', headers=headers)

mzj = MozillaCookieJar() # 创建MozillaCookieJar
handler = HTTPCookieProcessor(mzj) # 传入
opener = build_opener(handler)
respone_1 = opener.open(request_1)
respone_2 = opener.open(request_2)
mzj.save('cookie.txt') # 保存Cookie
# 同时可以使用mzj.load('filepath')的形式进行加载Cookie

URLError

在发送请求时有可能会发生异常,此时可以进行捕捉URLError。

from urllib.request import urlopen, Request, build_opener, HTTPHandler
from fake_useragent import UserAgent
from urllib.parse import urlencode
from urllib.error import URLError
import ssl

ua = UserAgent().chrome
headers = {
    "User-Agent": ua,
 }

request_1 = Request('https://www.baidu.com/1111asd', headers=headers)

opener = build_opener(HTTPHandler())
try:
    respone_1 = opener.open(request_1)

except URLError as err:
    print(err.code)
# 发生具体的错误可以通过debug来查看URLError的具体值,从而做出判断

Requests

Requests是一个类似于urllib的库,可以更方便的进行解析url等操作。

import requests as rq
  1. 基本请求

rq.get()
rq.post()
rq.delete()
rq.put()
rq.head()
rq.options()

  1. get请求

直接rq.get(url, params)参数可以不进行urlenode

import requests as rq
params = {
   'q': '你好世界'
}
responce = rq.get('https://www.bing.com/search', params=params)
print(responce.text)
  1. post请求
import requests as rq
form_data = {
    # 写参数
}
responce = rq.post('url', data=form_data)

自定义请求头和原来是一样的,传一个header的参数就行。

  1. session
  2. 使用session可以保存一个会话,同时这个会话中也会自动保存cookie
import requests as rq
s = rq.session()
s.get('https://www.bing.com')

以及其他一些列简单方方便的操作,详细的操作可以看这里

数据提取

beautifulsoup

使用beautifulsoup可以轻松地解析提取html,css等文件的信息。
首先先创建一个beautifulsoup对象。

import re
import requests
from bs4 import BeautifulSoup

s = """<div id="v_desc" class="video-desc report-wrap-module report-scroll-module" scrollshow="true">
            <div class="tip-info">
            </div>
            
            <div class="info">不拉不拉不拉
            </div><!---->
            
            <div report-id="abstract_spread" class="btn">
                <span>展开更多</span>
            </div>
        </div>"""

bs = BeautifulSoup(s, 'lxml') # lxml是解析器

可以直接使用bs. 标签的形式获取第一个匹配到的标签。

print(bs.span)
"""
输出:
<span>展开更多</span>
"""

查看内容:bs.span.text或者bs.span.string
查看某个标签的属性。

print(bs.div.attrs) # 所有属性
print(bs.div['class']) # 指定属性
"""
{'id': 'v_desc', 'class': ['video-desc', 'report-wrap-module', 'report-scroll-module'], 'scrollshow': 'true'}
['video-desc', 'report-wrap-module', 'report-scroll-module']
"""

使用find_all可以匹配所有同种类型的标签

print(bs.find_all('div'))

可以传入一个列表来同时匹配多种

print(bs.find_all(['div', 'span']))

同时也可以指定特定属性的值

print(bs.find_all('div', class_='tip-info')) # 当属性为class为了避免和关键字冲突所以要加下划线

也可以使用正则来进行匹配

print(bs.find_all('id', class_=re.compile(r'.+')))

更多的可以看这里

xpath

首先可以安装一个Chrome的插件,Xpath helper来辅助使用Xpath。
谷歌商店链接
安装后到指定页面点击就可以出来调试框
在这里插入图片描述
关于Xpath,和beautifulsoup一样可以进行标签属性,内容的提取。
首先Xpath,定义了一些东西,由于标签和标签之间是从属关系,所以所有标签和他们的关系构成了一个树形数据结构,Xpath就利用这种结构来进行检索。

Xpath有自己的语法,详细的可以看W3school本人太菜,所以就记一些比较主要的。

表达式作用
nodename选取此节点的所有子节点。
/从根节点选取。
//不考虑位置的去选择节点
.选择当前节点。
选择当前节点的父节点。

比如想要上面那个网页的head的内容,就可以这么写
在这里插入图片描述
也可以从任意位置开始,直接//title
在比如我们想匹配POJ这个信息,就可以先找到它。
在这里插入图片描述
我们直接输入//a但是它的输出确除了POJ还有ZOJ等一堆信息,这说明这样不足以确定它。
此时就可以使用谓语来帮助我们进一步的去明确信息,具体可以看W3school,有点像numpy的fancy index。

我们可以返回上一层到div,然后到他下面的span,然后选择第一个span,然后再选择a
在这里插入图片描述
可以发现此时返回的结果就是唯一的了。
在代码中可以这么使用

import re
import requests
from lxml import etree
s = """<div id="v_desc" class="video-desc report-wrap-module report-scroll-module" scrollshow="true">
            <div class="tip-info">
                <!--错的-->
            </div>
            
            <div class="info">不拉不拉不拉
            </div><!---->
            
            <div report-id="abstract_spread" class="btn">
                <span>展开更多</span>
            </div>
        </div>"""

e = etree.HTML(s)
print(e.xpath('//div[@report-id="abstract_spread"]/span/text()'))
"""
输出:
['展开更多']
"""

除此之外,在审查元素中可以直接右键复制Xpath路径
在这里插入图片描述

jsonpath

Jsonpath顾名思义是专门针对JSON进行数据处理的库与Xpath相同,Jsonpath也有自己的语法。大致如下。
在这里插入图片描述
这是一个Xpath与Jsonpath的语法对比图。其中n/a就是不存在。
除此之外可以在这里直接进行Jsonpath的语法测试这里,同时可以在这里解析Json
比如这个例子

{
    "firstName":"John",
    "lastName":"doe",
    "age":26,
    "address":{
        "streetAddress":"naist street",
        "city":"Nara",
        "postalCode":"630-0192"
    },
    "phoneNumbers":[
        {
            "type":"iPhone",
            "number":"0123-4567-8888"
        },
        {
            "type":"home",
            "number":"0123-4567-8910"
        }
    ]
}

比如想要获取所有type的值可以这么写

$[phoneNumbers].[type]

想要使用Jsonpath首先先安装它

conda install jsonpath

然后导入

import re
import requests
import json
from jsonpath import jsonpath

s = """{
    "firstName":"John",
    "lastName":"doe",
    "age":26,
    "address":{
        "streetAddress":"naist street",
        "city":"Nara",
        "postalCode":"630-0192"
    },
    "phoneNumbers":[
        {
            "type":"iPhone",
            "number":"0123-4567-8888"
        },
        {
            "type":"home",
            "number":"0123-4567-8910"
        }
    ]
}"""

先需要知道如何进行加载和保存Json
加载需要用到loads(把字符串的Json转成Python的字典),保存需要用到dumps(把Python的字典转成一个Json字符串)

import re
import requests
import json
from jsonpath import jsonpath

s = """{
    "firstName":"John",
    "lastName":"doe",
    "age":26,
    "address":{
        "streetAddress":"naist street",
        "city":"Nara",
        "postalCode":"630-0192"
    },
    "phoneNumbers":[
        {
            "type":"iPhone",
            "number":"0123-4567-8888"
        },
        {
            "type":"home",
            "number":"0123-4567-8910"
        }
    ]
}"""
obj = json.loads(s)
print(obj)
print(json.dumps(obj))

知道了这些就可以使用Jsonpath了。

import re
import requests
import json
from jsonpath import jsonpath

s = """{
    "firstName":"John",
    "lastName":"doe",
    "age":26,
    "address":{
        "streetAddress":"naist street",
        "city":"Nara",
        "postalCode":"630-0192"
    },
    "phoneNumbers":[
        {
            "type":"iPhone",
            "number":"0123-4567-8888"
        },
        {
            "type":"home",
            "number":"0123-4567-8910"
        }
    ]
}"""
print(jsonpath(json.loads(s), '$.[phoneNumbers].[type]')) 
"""
输出:
['iPhone', 'home']
"""

Selenium

配置

Selenium可以模拟真实的浏览器,在爬虫中可以解决Js渲染问题。
首先安装Selenium库

conda install selenium

然后还需要安装对应浏览器的driver,如果没有这个driver就没法进行对浏览器的控制,这里我使用的浏览器的chrome。
首先需要找到对应的版本
在这里插入图片描述
然后来到这个网站下载对应的版本
在这里插入图片描述
然后把下载好的文件解压,把对应的可执行文件放到Python安装的位置,放到和lib同一级的目录下
在这里插入图片描述
这样就配置好了,接下来是使用。
关于selenium的文档可以看这个

Hello World测试安装是否成功

import re
import requests
from selenium import webdriver # 导入webdriver

driver = webdriver.Chrome() # 使用Chromedirver
driver.get('https://www.baidu.com/') # 访问百度

如果打开了浏览器而且跳转到了百度,那么就是成功了。

简单用法

获取渲染后的页面代码

import re
import requests
from selenium import webdriver

driver = webdriver.Chrome()
driver.get('https://www.baidu.com')

with open('baidu.html', 'w', encoding='utf-8') as s:
    s.write(driver.page_source) # page_source就可以获取渲染后的代码 

查找一个元素,并且填写数据

import re
import requests
from selenium import webdriver

driver = webdriver.Chrome()
driver.get('https://www.baidu.com')
e = driver.find_element_by_xpath(r'//input[@id="kw"]') # 使用xpath的方式查找
e.send_keys(r'琪露诺') # 传入参数
e.click() # 点击

除了使用xpath查找,还有其他的方式,比如name,id之类的

如果想要不打开浏览器,同时又获取到内容,则可以加这么一行代码,这样就可以关闭启动头

options = webdriver.chrome.options.Options()
options.add_argument('--headless')
driver = webdriver.Chrome(chrome_options=options) # 传入上述的参数

但是一旦关闭这个,输出就会多出一堆console,此时如果觉着烦,也可以把console也给关了,具体方法可以看这一篇博客Selenium关闭console

还有一个需要注意的是,加载网页需要花费一定时间,而加载时线程并不会阻塞,所以会继续执行下面的代码,这就会导致可能还没加完成就去获取网页,此时应该让线程sleep一会进行等待。

import re
import requests
from selenium import webdriver
import time

options = webdriver.chrome.options.Options()
options.add_argument('--headless')
driver = webdriver.Chrome(chrome_options=options)
driver.get('https://www.baidu.com')

time.sleep(0.5)
driver.find_element_by_xpath(r'//input[@id="kw"]').send_keys(r'琪露诺')
driver.find_element_by_xpath(r'//input[@type="submit"]').click()
time.sleep(0.5)

with open('Chiruno.html', 'w', encoding='utf-8') as s:
    s.write(driver.page_source)
driver.quit()

但是这样做显然很麻烦,如果网络有波动,就又会报错了。此时可以这么写

import re
import requests
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
import time

options = webdriver.chrome.options.Options()
options.add_argument('--headless')
driver = webdriver.Chrome(chrome_options=options)
driver.get('https://www.baidu.com')
try:
    WebDriverWait(driver, 5).until( # 加入这一行,用来阻塞线程,直到找到某个指定的元素,第二个参数是最长等待时间
        EC.presence_of_element_located((By.XPATH, '//input[@id="kw"]')) # 参数第一个是查找的方式,第二个是对应查找方式的参数
    )
    driver.find_element_by_xpath(r'//input[@id="kw"]').send_keys(r'琪露诺')
    driver.find_element_by_xpath(r'//input[@type="submit"]').click()

    WebDriverWait(driver, 5).until(
        EC.presence_of_element_located((By.XPATH, '//div[@class="nums new_nums"]/span'))
    )
    with open('Chiruno.html', 'w', encoding='utf-8') as s:
        s.write(driver.page_source)
finally:
    driver.quit()

更详细的,为什么这么做,可以看这个https://selenium-python-zh.readthedocs.io/en/latest/waits.html

有些数据只有在在向下滚动时才会出现,此时就需要去操纵滚动条。而想要操纵滚动条则要行一段js那就是
window.scrollBy()
他有两个参数,第一个是向右和向下滚动的元素个数,如果是负数则方向相反。而使用execute_script方法则可以执行这一函数。
比如

driver.get('https://www.nowcoder.com/')
driver.execute_script('window.scrollBy(0,500)')

scrapy

scrapy是一个专门用于爬虫的框架,提供了一套很完备的流程,还支持分布式爬取的很强的技术。
scrapy的安装可以看这里
我装上后运行报警告:
You do not have a working installation of the service_identity module
此时查了一下包发现也装了,然后发现其实是service_identity太老了,直接更新一下就好了

pip install --upgrade service_identity

基本使用

首先在控制台输入scrapy然后看一下输出信息,确保自己安装成功。
然后就开始创建一个项目, 创建方式:

scrapy startproject ‘项目名’
比如 scrapy startproject ScrapySpider

此时在当前路径下就会生成一个项目,然后根据提示创建第一个爬虫

scrapy genspider ‘爬虫名’ url
scrapy genspider baidu www.baidu.com

然后完成了
在这里插入图片描述
然后就是这些文件,他们分别是干啥的。
scrapy的工作结构图如下:
在这里插入图片描述
这是baidu.py文件,从上面的结构图可以看出,他可以进行发送请求和解析响应,并且把解析的内容通过items送给Pipline

import scrapy

class BaiduSpider(scrapy.Spider): # 这是爬虫本体
    name = 'baidu' # name是这个爬虫的名字
    allowed_domains = ['wwww.baidu.com'] # 这里是允许爬取的的域名,也就是只能在这个域名下爬取,这样可以防止爬到外链
    start_urls = ['http://wwww.baidu.com/'] # 这里是就是最开始爬时用的url

    def parse(self, response): # 这个方法用于解析爬回来的东西,response就是返回爬回来的东西
        # print(response.text)
        pass

item文件主要用于做数据处理和数据清洗使用,他里面定义了具体的Item类型,像字典一样的数据结构。

# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html

import scrapy


class ScrapyspiderItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    pass

middleware则是下载中间介,他可以控制最后传给spider的response等,是一个处理spider的钩子框架。

Pipline则是用于保存爬取的数据

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html


# useful for handling different item types with a single interface
from itemadapter import ItemAdapter


class ScrapyspiderPipeline:
    def process_item(self, item, spider):
        return item

setting则是配置爬虫的配置文件,里面有很多参数
他们的参数可以看这个
这里我记一部分比较常用的。

BOT_NAME = 'ScrapySpider' # 项目名称,用于构建log和默认UA

SPIDER_MODULES = ['ScrapySpider.spiders'] # Scrapy进行执行的Spider列表里面写的是包的路径

USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.104 Safari/537.36' # UA,默认其实不是这个,这里我改了

ROBOTSTXT_OBEY = True # 是否检查此信息允许爬取,如果检查到不允许则就不爬(True是检查)。

CONCURRENT_REQUESTS = 32 # 并发请求的最大值

DOWNLOAD_DELAY = 1 #控制访问频率,单位是秒

COOKIES_ENABLED = False # 是否携带Cookie

DEFAULT_REQUEST_HEADERS = { # 默认请求头
   'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
   'Accept-Language': 'en',
}

DOWNLOADER_MIDDLEWARES = { # 配置要是用的下载中间介
    'ScrapySpider.middlewares.ScrapyspiderDownloaderMiddleware': 543,
}

ITEM_PIPELINES = { # 配置使用的Pipline
    'ScrapySpider.pipelines.ScrapyspiderPipeline': 300,
}

看完文件之后就需要知道怎么去启动运行整个项目了,可以使用scrapy指令

scrapy crawl ‘爬虫名’(就是之前那个name变量)
比如 scrapy crawl baidu

也可以直接写一个文件然后执行。
在这里插入图片描述

Selector

爬虫爬取到HTML后会返回一个response,他就是Parse中的response的值,它实际上就是一个HtmlResponse实例,可以使用它来进行信息的提取。
事实上response内置了一个selector,可以使用这个selector进行提取。
可以直接进行xpath和css和re进行提取。

import scrapy


class BaiduSpider(scrapy.Spider):
    name = 'baidu'
    allowed_domains = ['baidu.com']
    start_urls = ['https://www.baidu.com/']

    def parse(self, response):
        print('XpathOut: ------------>' + str(response.xpath('//a/div[@class="title-text c-font-medium c-color-t"]/text()')))
"""
OUtput:
XpathOut: ------------>[<Selector xpath='//a/div[@class="title-text c-font-medium c-color-t"]/text()' data='百度热榜'>]
"""

可以发现此时返回的是一个selector的对象,如果想要里面具体的值,则需要使用extract或者get方法把它抽取出来。

import scrapy


class BaiduSpider(scrapy.Spider):
    name = 'baidu'
    allowed_domains = ['baidu.com']
    start_urls = ['https://www.baidu.com/']

    def parse(self, response):
        print('XpathOut: ------------>' + str(response.xpath('//a/div[@class="title-text c-font-medium c-color-t"]/text()').get()))

这里get只会返回一个值,如果匹配返回的十多个selector的话并且想要提取里面的值,则需要用getall方法。

Pipeline&Item

Parse中返回的数据可以直接被用于保存成文件。

import scrapy


class BaiduSpider(scrapy.Spider):
    name = 'baidu'
    allowed_domains = ['baidu.com']
    start_urls = ['https://www.baidu.com/']

    def parse(self, response):
        titles = {i: title for i, title in enumerate(response.xpath('//span[@class="title-content-title"]/text()').getall())} # 生成一个字典
        return titles # 返回一个字典或者item对象

然后执行命令

scrapy crawl baidu -o baidu.csv

就可以得到一个csv文件。
Item其实就像是scrapy中的字典,文档说它copy了dict的大量的方法包括初始化dict的方法。更多可以看官方文档的item条https://docs.scrapy.org/en/latest/topics/items.html#

parse的信心除了返回之外,和可以直接推到Pipeline中,使用yield关键字即可。
不过首先需要指定推送到哪个Pipline中,这里就需要修改setting中的ITEM_PIPELINES。指定处理信息的方法

ITEM_PIPELINES = {
   'ScrapySpider.pipelines.ScrapyspiderPipeline': 300,
}
import scrapy
import re

class NowcoderSpider(scrapy.Spider):
    name = 'nowcoder'
    allowed_domains = ['nowcoder.com/discuss']
    start_urls = ['http://nowcoder.com/discuss/']

    def parse(self, response):
        tmp = response.xpath('//div[@class="discuss-main clearfix"]/a[@rel="prefetch"][1]/text()').getall()
        titles = [re.sub(r'\n', '', title) for title in tmp]
        titles = list(filter(lambda x: len(x) > 0, titles))
        links = ['www.nowcoder.com' + link for link in response.xpath('//div[@class="discuss-main clearfix"]/a[@rel="prefetch"][1]/@href').getall()]
        for title, link in zip(titles, links):
            yield {'title': title, 'link': link} # 使用yield把数据推导Pipline里。

注意和返回值一样,yield的数据也只能是字典或者item对象

如果要传Item则需要自己定义一个Item子类。

import scrapy


class DiscussItem(scrapy.Item):
    links = scrapy.Field() # 这个Item里有links和titles属性
    titles = scrapy.Field()

然后导入yield这个Item

import scrapy
import re
from ..items import DiscussItem

class NowcoderSpider(scrapy.Spider):
    name = 'nowcoder'
    allowed_domains = ['nowcoder.com/discuss']
    start_urls = ['http://nowcoder.com/discuss/']

    def parse(self, response):
        tmp = response.xpath('//div[@class="discuss-main clearfix"]/a[@rel="prefetch"][1]/text()').getall()
        titles = [re.sub(r'\n', '', title) for title in tmp]
        titles = list(filter(lambda x: len(x) > 0, titles))
        links = ['www.nowcoder.com' + link for link in response.xpath('//div[@class="discuss-main clearfix"]/a[@rel="prefetch"][1]/@href').getall()]
        yield DiscussItem(links=links, titles=titles) # 创建一个DiscussItem,然后可以直接以key,value的方式传入创建,

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值