python爬虫总结,看这篇就够了

文章目录

爬虫课程

urllib

urlopen函数

from urllib import request
resp=request.urlopen('http://www.baidu.com')
print(resp.read())
read()读取所有数据
readline()只读取一行
readlines()逐行列表形势读取

urlretrieve函数

request.urlretrieve('http://www.xxx.com','index.html')

将网页中的内容保存到本地文件中

urlencode函数和parse_qs函数

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())

urlparse函数和urlsplit函数

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')

request.Request函数

# 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'))

request.ProxyHandler函数

# 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登陆实例->人人网

其中涉及到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)


保存cookie到本地

#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)

加载本地cookie

#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)

requests库

# 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)

request添加代理

# encoding utf-8
import requests

proxy = {
    
    
    'http': '182.46.251.204:9999'
}
resp = requests.get('http://httpbin.org/ip', proxies=proxy)
print(resp.content)

绕过ssl证书

#添加verify=False绕过ssl证书验证
resp = requests.get('http://httpbin.org/ip', proxies=proxy,verify=False)

XPath使用

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函数使用
#此处enumerate函数可以返回除了基本信息以外的索引号
for index, info in enumerate(infos):
	print(info)
	print(index)
replace函数和strip函数
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())

正则表达式验证url

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='<h1>标题</h1>'
ret=re.match('<.+>',text)
print(ret.group())

#打印结果为:<h1>标题</h1>
import re
#非贪婪模式
text='<h1>标题</h1>'
#在匹配过程中最后添加?号
ret=re.match('<.+?>',text)
print(ret.group())

#打印结果为:<h1>

匹配0-100的数字

import re
#匹配0-100之间的数字
text='100'
#其中第一位为1-9,第二位有或者没有,所以加'?',但是如果是多位的话还可以匹配,所以加$,两位数字以后结尾或者是结果为100时可以匹配
ret=re.match('[1-9]\d?$|100$',text)
print(ret.group())

正则表达式中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')

正则表达式中findall函数的用法

找出所有满足条件的,返回的为列表

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函数的用法

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)

正则表达式中split函数的用法

import re
text='hellow wor adv'
ret=re.split(' ',text)
print(ret)

编译正则表达式-- re.compile

对于经常用到的正则表达式可以先用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)

匹配包括换行符内的所有字符re.DOTALL=re.S

Python的正则表达式模块re,有一个re.DOTALL的参数。默认情况下,正则表达式中的dot(.),表示所有除了换行的字符,加上re.DOTALL参数后,就是真正的所有字符了,包括换行符(\n)

json dump和dumps函数

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 load函数和loads函数

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文件读写操作

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])

csv写入操作

# 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()


python多线程操作

传统代码

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()


Lock版本的生产者模式和消费者模式

知识点

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()


Condition锁机制

# 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) 创建一个先进先出的队列。

  1. gsize():返回队列的大小。
  2. empty():判断队列是否为空。
  3. full():判断队列是否满了。
  4. get():从队列中取最后一个数据
  5. put():将一个数据放到队列中。

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()


selenium和chromedriver驱动浏览器

selenium和chromedriver下载使用

下载地址: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()

selenium定位元素7种方式

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')

selenium操作表单元素

# 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()

select操作

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()

selenium行为链

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()

selenium操作cookies

#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()

selenium显示等待和隐式等待

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()

selenium打开、切换多窗口

# 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)

selenium设置代理

# 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')

selenium webElement

# 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')

酷狗爬取Top500(列表生成器,去除列表中空元素)

# 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()


scrapy 使用

1、创建scrapy项目

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:配置文件

Scrapy快速入门

安装和文档:

  1. 安装:通过pip install scrapy即可安装。
  2. Scrapy官方文档:http://doc.scrapy.org/en/latest
  3. Scrapy中文文档:http://scrapy-chs.readthedocs.io/zh_CN/latest/index.html

注意:

  1. 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安装。
  2. 如果在windows系统下,提示这个错误ModuleNotFoundError: No module named 'win32api',那么使用以下命令可以解决:pip install pypiwin32

快速入门:

创建项目:

要使用Scrapy框架创建项目,需要通过命令来创建。首先进入到你想把这个项目存放的目录。然后使用以下命令创建:

scrapy startproject [项目名称]

目录结构介绍:

以下介绍下主要文件的作用:

  1. items.py:用来存放爬虫爬取下来数据的模型。
  2. middlewares.py:用来存放各种中间件的文件。
  3. pipelines.py:用来将items的模型存储到本地磁盘中。
  4. settings.py:本爬虫的一些配置信息(比如请求头、多久发送一次请求、ip代理池等)。
  5. scrapy.cfg:项目的配置文件。
  6. spiders包:以后所有的爬虫,都是存放到这个里面。

使用Scrapy框架爬取糗事百科段子:

使用命令创建一个爬虫:

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,然后在这个类中定义三个属性和一个方法。

  1. name:这个爬虫的名字,名字必须是唯一的。
  2. allow_domains:允许的域名。爬虫只会爬取这个域名下的网页,其他不是这个域名下的网页会被自动忽略。
  3. start_urls:爬虫从这个变量中的url开始。
  4. parse:引擎会把下载器下载回来的数据扔给爬虫解析,爬虫再把数据传给这个parse方法。这个是个固定的写法。这个方法的作用有两个,第一个是提取想要的数据。第二个是生成下一个请求的url。

修改settings.py代码:

在做一个爬虫之前,一定要记得修改setttings.py中的设置。两个地方是强烈建议设置的。

  1. ROBOTSTXT_OBEY设置为False。默认是True。即遵守机器协议,那么在爬虫的时候,scrapy首先去找robots.txt文件,如果没有找到。则直接停止爬取。
  2. DEFAULT_REQUEST_HEADERS添加User-Agent。这个也是告诉服务器,我这个请求是一个正常的请求,不是一个爬虫。

完成的爬虫代码:

  1. 爬虫部分代码:

     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
    
    
  2. items.py部分代码:

     import scrapy
     class QsbkItem(scrapy.Item):
         author = scrapy.Field()
         content = scrapy.Field()
    
    
  3. 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项目。需要在终端,进入项目所在的路径,然后scrapy crawl [爬虫名字]即可运行指定的爬虫。如果不想每次都在命令行中运行,那么可以把这个命令写在一个文件中。以后就在pycharm中执行运行这个文件就可以了。比如现在新创建一个文件叫做start.py,然后在这个文件中填入以下代码:

from scrapy import cmdline

cmdline.execute("scrapy crawl qsbk".split())

scrapy自己总结笔记

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,
    }
    
    

JsonItemExporter和JsonLinesItemExporter

保存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('爬虫结束了。。。')


CrawlSpider

在上一个糗事百科的爬虫案例中。我们是自己在解析完整个页面后获取下一页的url,然后重新发送一个请求。有时候我们想要这样做,只要满足某个条件的url,都给我进行爬取。那么这时候我们就可以通过CrawlSpider来帮我们完成了。CrawlSpider继承自Spider,只不过是在之前的基础之上增加了新的功能,可以定义爬取的url的规则,以后scrapy碰到满足条件的url都进行爬取,而不用手动的yield Request

CrawlSpider爬虫:

创建CrawlSpider爬虫:

之前创建爬虫的方式是通过scrapy genspider [爬虫名字] [域名]的方式创建的。如果想要创建CrawlSpider爬虫,那么应该通过以下命令创建:

scrapy genspider -t crawl [爬虫名字] [域名]

LinkExtractors链接提取器:

使用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
)

主要参数讲解:

  • allow:允许的url。所有满足这个正则表达式的url都会被提取。
  • deny:禁止的url。所有满足这个正则表达式的url都不会被提取。
  • allow_domains:允许的域名。只有在这个里面指定的域名的url才会被提取。
  • deny_domains:禁止的域名。所有在这个里面指定的域名的url都不会被提取。
  • restrict_xpaths:严格的xpath。和allow共同过滤链接。

Rule规则类:

定义爬虫的规则类。以下对这个类做一个简单的介绍:

class scrapy.spiders.Rule(
    link_extractor, 
    callback = None, 
    cb_kwargs = None, 
    follow = None, 
    process_links = None, 
    process_request = None
)

主要参数讲解:

  • link_extractor:一个LinkExtractor对象,用于定义爬取规则。
  • callback:满足这个规则的url,应该要执行哪个回调函数。因为CrawlSpider使用了parse作为回调函数,因此不要覆盖parse作为回调函数自己的回调函数。
  • follow:指定根据该规则从response中提取的链接是否需要跟进。
  • process_links:从link_extractor中获取到链接后会传递给这个函数,用来过滤不需要爬取的链接。

微信小程序社区CrawlSpider案例

主程序代码如下

# -*- 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。

scrapy shell

1、可以方便的做一些数据提取的测试代码。

2、如果想要执行scrapy命令,要先进入到scrapy所在的环境中。

3、如果想要夺取某个项目的配置信息,那么应该先禁图到这个项目中,再执行scrapy shell命令。

scrapy shell 'url地址'

scrapy模拟登陆人人网

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)

scrapy 下载文件和图片

下载文件的 Files Pipeline

当使用Files Pipeline 下载文件的时候,按照以下步骤来完成:

  1. 定义好一个Items ,然后在这个 item 中定义两个属性,分别为 file_urls 以及 filesfile_urls是用来存储需要下载的图片的url链接,需要给一个列表。
  2. 当文件下载完成后,会把文件下载的相关信息存储到itemfiles属性中。比如下载路径、下载的url和文件的校验码等。
  3. 在配置文件 settings.py中配置 FILES_STORE,这个配置是用来设置文件下载下来的路径。
  4. 启动 pipeline:在 ITEM_PIPELINES中设置 scrapy.pipelines.files.FilesPipeline:1

下载图片的 Image Pipeline

当使用Image Pipeline下载文件的时候,按照以下步骤来完成:

  1. 定义好一个Item,然后在这个 Item 中定义两个属性,分别为 image_urls以及 imagesimage_urls是用来存储需要下载的图片的url链接,需要给一个列表。
  2. 当文件下载完成后,会把文件下载的相关信息存储到itemimages属性中。比如下载路径、下载的url和文件的校验码等。
  3. 在配置文件 settings.py中配置 IMAGES_STORE,这个配置是用来设置文件下载下来的路径。
  4. 启动 pipeline:在 ITEM_PIPELINES中设置 scrapy.pipelines.images.ImagesPipeline:1

map函数

srcs = list(map(lambda x: response.urljoin(x), srcs))

map(function, iterable, ...)

  • function – 函数
  • iterable – 一个或多个序列
map(lambda x: x ** 2, [1, 2, 3, 4, 5])  # 使用 lambda 匿名函数
[1, 4, 9, 16, 25]

User-Agent随机请求头

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

scrapy通过twisted实现异步存入数据库

代码如下:

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)

猜你喜欢

转载自blog.csdn.net/yuanxu8877/article/details/127607310