├── .gitignore
├── LICENSE
├── PPT课件
├── 第10章 Android原生App爬虫.pptx
├── 第11章 Scrapy.pptx
├── 第12章 Scrapy高级应用.pptx
├── 第13章 爬虫开发中的法律和道德问题.pptx
├── 第1章 绪论.pptx
├── 第2章 Python基础.pptx
├── 第3章 正则表达式与文件操作.pptx
├── 第4章 简单的网页爬虫开发.pptx
├── 第5章 高性能HTML内容解析.pptx
├── 第6章 Python与数据库.pptx
├── 第7章 异步加载与请求头.pptx
├── 第8章 模拟登录与验证码.pptx
└── 第9章 抓包与中间人爬虫.pptx
├── Python初学者常犯的错误及其解决办法.md
├── README.md
├── etc
├── Dockerfile
└── 使用Docker Swarm搭建分布式爬虫集群.md
├── 勘误表.md
├── 第10章
└── program
│ ├── .idea
│ ├── inspectionProfiles
│ │ └── profiles_settings.xml
│ ├── misc.xml
│ ├── modules.xml
│ ├── program.iml
│ └── workspace.xml
│ ├── example_automatic_wechat.py
│ ├── example_launch_wechat.py
│ ├── example_multi_device.py
│ ├── example_taptap.py
│ ├── homework_boss.py
│ ├── phone_list
│ └── target
├── 第11章
└── program
│ ├── .idea
│ ├── baidu.iml
│ ├── misc.xml
│ ├── modules.xml
│ └── workspace.xml
│ ├── Pipfile
│ ├── Pipfile.lock
│ ├── baidu
│ ├── __init__.py
│ ├── items.py
│ ├── pipelines.py
│ ├── settings.py
│ └── spiders
│ │ ├── BlogSpider.py
│ │ ├── __init__.py
│ │ ├── example.py
│ │ ├── exercise11_1.py
│ │ ├── exercise11_2.py
│ │ └── exercise11_3.py
│ ├── main.py
│ └── scrapy.cfg
├── 第12章
└── program
│ ├── AdvanceSpider
│ ├── .idea
│ │ ├── AdvanceSpider.iml
│ │ ├── inspectionProfiles
│ │ │ └── profiles_settings.xml
│ │ ├── misc.xml
│ │ ├── modules.xml
│ │ └── workspace.xml
│ ├── AdvanceSpider
│ │ ├── __init__.py
│ │ ├── items.py
│ │ ├── middlewares.py
│ │ ├── pipelines.py
│ │ ├── settings.py
│ │ └── spiders
│ │ │ ├── IpChecker.py
│ │ │ ├── __init__.py
│ │ │ ├── checkSpider.py
│ │ │ ├── exceptionSpider.py
│ │ │ ├── loginSpider.py
│ │ │ ├── middlewareSpider.py
│ │ │ └── seleniumSpider.py
│ ├── chromedriver
│ ├── helper.py
│ ├── main.py
│ ├── scrapy.cfg
│ └── setup.py
│ ├── DeploySpider
│ ├── .idea
│ │ ├── DeploySpider.iml
│ │ ├── inspectionProfiles
│ │ │ └── profiles_settings.xml
│ │ ├── misc.xml
│ │ ├── modules.xml
│ │ └── workspace.xml
│ ├── DeploySpider
│ │ ├── __init__.py
│ │ ├── items.py
│ │ ├── middlewares.py
│ │ ├── pipelines.py
│ │ ├── settings.py
│ │ └── spiders
│ │ │ ├── Example.py
│ │ │ └── __init__.py
│ ├── scrapy.cfg
│ ├── scrapy_template.cfg
│ └── setup.py
│ └── Requester
│ ├── .idea
│ ├── Requester.iml
│ ├── inspectionProfiles
│ │ └── profiles_settings.xml
│ ├── misc.xml
│ ├── modules.xml
│ └── workspace.xml
│ ├── Bat_Deploy.py
│ └── Launcher.py
├── 第2章
└── program
│ ├── .idea
│ ├── misc.xml
│ ├── modules.xml
│ ├── program.iml
│ └── workspace.xml
│ ├── Dict_Set.py
│ ├── ForLoop.py
│ ├── Guess.py
│ ├── If_Statement.py
│ ├── IntFloatVariable.py
│ ├── Robot.py
│ ├── String_List_Tuple.py
│ ├── WhileLoop.py
│ ├── class_example.py
│ ├── default_fun.py
│ ├── function.py
│ └── input_output.py
├── 第3章
└── program
│ ├── .idea
│ ├── chapter_3.iml
│ ├── inspectionProfiles
│ │ └── profiles_settings.xml
│ ├── misc.xml
│ ├── modules.xml
│ └── workspace.xml
│ ├── CsvOperator.py
│ ├── FileOperator.py
│ ├── Tieba.py
│ ├── new.csv
│ ├── new.txt
│ ├── regex.py
│ ├── result.csv
│ ├── source.txt
│ ├── text.txt
│ └── tieba.csv
├── 第4章
└── program
│ ├── .idea
│ ├── inspectionProfiles
│ │ └── profiles_settings.xml
│ ├── misc.xml
│ ├── modules.xml
│ ├── program.iml
│ └── workspace.xml
│ ├── Chapter_4_animal.py
│ ├── Chapter_4_compare.py
│ ├── Chapter_4_dummy.py
│ ├── Chapter_4_example.py
│ └── 动物农场
│ ├── 第一章.txt
│ ├── 第七章.txt
│ ├── 第三章.txt
│ ├── 第九章.txt
│ ├── 第二章.txt
│ ├── 第五章.txt
│ ├── 第八章.txt
│ ├── 第六章.txt
│ ├── 第十章.txt
│ └── 第四章.txt
├── 第5章
└── program
│ ├── .idea
│ ├── inspectionProfiles
│ │ └── profiles_settings.xml
│ ├── misc.xml
│ ├── modules.xml
│ ├── program.iml
│ └── workspace.xml
│ ├── Chapter_5_bs4.py
│ ├── Chapter_5_damai.py
│ ├── Chapter_5_tieba.py
│ ├── Chapter_5_xpath.py
│ ├── Chapter_5_xpath_special.py
│ └── result.csv
├── 第6章
└── program
│ ├── .idea
│ ├── code.iml
│ ├── inspectionProfiles
│ │ └── profiles_settings.xml
│ ├── misc.xml
│ ├── modules.xml
│ └── workspace.xml
│ ├── chapter_example_cqud.py
│ ├── chapter_mongo_opt.py
│ ├── chapter_update_opt.py
│ ├── content_crawler.py
│ └── url_crawler.py
├── 第7章
└── program
│ ├── .idea
│ ├── inspectionProfiles
│ │ └── profiles_settings.xml
│ ├── misc.xml
│ ├── modules.xml
│ ├── program.iml
│ └── workspace.xml
│ ├── example_advanced.py
│ ├── example_ajax_request.py
│ ├── example_fake_ajax.py
│ ├── example_headers.py
│ ├── example_json.py
│ ├── example_login.py
│ ├── example_multi_requests.py
│ ├── example_selenium.py
│ └── homework_letv.py
├── 第8章
└── program
│ ├── .idea
│ ├── inspectionProfiles
│ │ └── profiles_settings.xml
│ ├── misc.xml
│ ├── modules.xml
│ ├── program.iml
│ └── workspace.xml
│ ├── captcha.png
│ ├── example_captcha.py
│ ├── example_captchakiller.py
│ ├── example_login.py
│ ├── example_login_zhihu.py
│ ├── example_tesseract.py
│ ├── example_zhihu.com.py
│ ├── guokr.png
│ ├── homework_guokr.py
│ └── 验证码.png
└── 第9章
└── program
├── .idea
├── inspectionProfiles
│ └── profiles_settings.xml
├── misc.xml
├── modules.xml
├── program.iml
└── workspace.xml
├── extract.py
├── generate_token.py
├── homework_keep.py
├── juejin.py
├── parse_one_site.py
├── parse_request.py
└── parse_response.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | .DS_Store
7 | # C extensions
8 | *.so
9 |
10 | # Distribution / packaging
11 | .Python
12 | build/
13 | develop-eggs/
14 | dist/
15 | downloads/
16 | eggs/
17 | .eggs/
18 | lib/
19 | lib64/
20 | parts/
21 | sdist/
22 | var/
23 | wheels/
24 | *.egg-info/
25 | .installed.cfg
26 | *.egg
27 | MANIFEST
28 |
29 | # PyInstaller
30 | # Usually these files are written by a python script from a template
31 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
32 | *.manifest
33 | *.spec
34 |
35 | # Installer logs
36 | pip-log.txt
37 | pip-delete-this-directory.txt
38 |
39 | # Unit test / coverage reports
40 | htmlcov/
41 | .tox/
42 | .coverage
43 | .coverage.*
44 | .cache
45 | nosetests.xml
46 | coverage.xml
47 | *.cover
48 | .hypothesis/
49 | .pytest_cache/
50 |
51 | # Translations
52 | *.mo
53 | *.pot
54 |
55 | # Django stuff:
56 | *.log
57 | local_settings.py
58 | db.sqlite3
59 |
60 | # Flask stuff:
61 | instance/
62 | .webassets-cache
63 |
64 | # Scrapy stuff:
65 | .scrapy
66 |
67 | # Sphinx documentation
68 | docs/_build/
69 |
70 | # PyBuilder
71 | target/
72 |
73 | # Jupyter Notebook
74 | .ipynb_checkpoints
75 |
76 | # pyenv
77 | .python-version
78 |
79 | # celery beat schedule file
80 | celerybeat-schedule
81 |
82 | # SageMath parsed files
83 | *.sage.py
84 |
85 | # Environments
86 | .env
87 | .venv
88 | env/
89 | venv/
90 | ENV/
91 | env.bak/
92 | venv.bak/
93 |
94 | # Spyder project settings
95 | .spyderproject
96 | .spyproject
97 |
98 | # Rope project settings
99 | .ropeproject
100 |
101 | # mkdocs documentation
102 | /site
103 |
104 | # mypy
105 | .mypy_cache/
106 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 kingname
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/PPT课件/第10章 Android原生App爬虫.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingname/SourceCodeOfBook/ab7275108994dca564905818b678bbd2f771c18e/PPT课件/第10章 Android原生App爬虫.pptx
--------------------------------------------------------------------------------
/PPT课件/第11章 Scrapy.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingname/SourceCodeOfBook/ab7275108994dca564905818b678bbd2f771c18e/PPT课件/第11章 Scrapy.pptx
--------------------------------------------------------------------------------
/PPT课件/第12章 Scrapy高级应用.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingname/SourceCodeOfBook/ab7275108994dca564905818b678bbd2f771c18e/PPT课件/第12章 Scrapy高级应用.pptx
--------------------------------------------------------------------------------
/PPT课件/第13章 爬虫开发中的法律和道德问题.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingname/SourceCodeOfBook/ab7275108994dca564905818b678bbd2f771c18e/PPT课件/第13章 爬虫开发中的法律和道德问题.pptx
--------------------------------------------------------------------------------
/PPT课件/第1章 绪论.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingname/SourceCodeOfBook/ab7275108994dca564905818b678bbd2f771c18e/PPT课件/第1章 绪论.pptx
--------------------------------------------------------------------------------
/PPT课件/第2章 Python基础.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingname/SourceCodeOfBook/ab7275108994dca564905818b678bbd2f771c18e/PPT课件/第2章 Python基础.pptx
--------------------------------------------------------------------------------
/PPT课件/第3章 正则表达式与文件操作.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingname/SourceCodeOfBook/ab7275108994dca564905818b678bbd2f771c18e/PPT课件/第3章 正则表达式与文件操作.pptx
--------------------------------------------------------------------------------
/PPT课件/第4章 简单的网页爬虫开发.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingname/SourceCodeOfBook/ab7275108994dca564905818b678bbd2f771c18e/PPT课件/第4章 简单的网页爬虫开发.pptx
--------------------------------------------------------------------------------
/PPT课件/第5章 高性能HTML内容解析.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingname/SourceCodeOfBook/ab7275108994dca564905818b678bbd2f771c18e/PPT课件/第5章 高性能HTML内容解析.pptx
--------------------------------------------------------------------------------
/PPT课件/第6章 Python与数据库.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingname/SourceCodeOfBook/ab7275108994dca564905818b678bbd2f771c18e/PPT课件/第6章 Python与数据库.pptx
--------------------------------------------------------------------------------
/PPT课件/第7章 异步加载与请求头.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingname/SourceCodeOfBook/ab7275108994dca564905818b678bbd2f771c18e/PPT课件/第7章 异步加载与请求头.pptx
--------------------------------------------------------------------------------
/PPT课件/第8章 模拟登录与验证码.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingname/SourceCodeOfBook/ab7275108994dca564905818b678bbd2f771c18e/PPT课件/第8章 模拟登录与验证码.pptx
--------------------------------------------------------------------------------
/PPT课件/第9章 抓包与中间人爬虫.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingname/SourceCodeOfBook/ab7275108994dca564905818b678bbd2f771c18e/PPT课件/第9章 抓包与中间人爬虫.pptx
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 《Python爬虫开发 从入门到实战》配套源代码
2 |
3 | ## 购书地址:
4 |
5 | * 京东:[https://item.jd.com/12436581.html](https://item.jd.com/12436581.html)
6 | * 当当:[http://product.m.dangdang.com/25349717.html](http://product.m.dangdang.com/25349717.html)
7 | * 亚马逊:[https://www.amazon.cn/dp/B07HGBRXFW](https://www.amazon.cn/dp/B07HGBRXFW)
8 |
9 | ## 我的公众号
10 |
11 | 
12 |
13 | # 如果你不知道做什么,那就学一门杂学吧
14 |
15 | ## 序言
16 |
17 | 这篇文章没有代码,请放心阅读。
18 |
19 | 多年以后,面对人工智能研究员那混乱不堪的代码,我会想起第一次和S君相见的那个遥远的下午。那时的B公司,还是一个仅有6个人的小团队,Mac和显示器在桌上依次排开,大家坐在一起,不需要称呼姓名,转过脸去,对方就知道你在和他说话。一切看起来都那么美好,我们所有人,都希望自己和这个公司能够一起成长。
20 |
21 | 彼时S君刚从加拿大回来,老板把他介绍给我们,于是S君作为数据产品经理跟我有了项目上的接触。
22 |
23 | 创业公司里面,每一个人都需要会很多的技艺,于是S君开始自学Python。
24 |
25 | 有一天,S君问我:“你玩MineCraft吗?“
26 |
27 | “玩,但我更喜欢在B站上看别人的世界。”我答道。
28 |
29 | “我觉得我现在写程序,像是在玩我的世界。”S君笑着说道。
30 |
31 | “是不是觉得你已经掌握了Python的基本语法,看着别人把Python用的溜溜转,而你自己却不知道用它来做什么?”
32 |
33 | “是这样的,你懂我。”
34 |
35 | “那你学一门杂学吧。”
36 |
37 | 于是S君被我诱拐过来跟我一起写爬虫。
38 |
39 | 后来,S君离开了B公司。
40 |
41 | 三个月后,我也离开了。
42 |
43 | 从此,我们再也没有见过。
44 |
45 | ## 编程最重要的能力是变通
46 |
47 | S君是一个老实孩子。
48 |
49 | 在开发一个爬虫的过程中,网站接口返回给他的数据看起来是JSON格式,于是他就用Python自带的JSON库去解析。结果解析失败了。因为这些所谓的看起来像JSON的东西,竟然没有双引号。
50 |
51 | 难道是JSON的超集?S君一通搜索,发现用YMAL库也许可以解析这种数据。于是安装YMAL库,一解析又报错。
52 |
53 | 难道这些数据直接就是Python的字典?于是S君用上了邪恶的eval。又报错,因为里面有null和小写的true。
54 |
55 | “你为什么不试一试直接用正则表达式呢?”我对S君说。
56 |
57 | “靠!”S君一拍桌子,旁边的老板吓得把搪瓷杯子里面的快乐水洒在了白衬衣上。
58 |
59 | 然后S君用正则表达式花了10秒钟结束了战斗。
60 |
61 | ## 写爬虫与三峡大坝
62 |
63 | 有一天,S君兴冲冲地跑来跟我说:“我体会到三峡大坝的伟大功能了!”
64 |
65 | “你是爬虫工程师还是水利工程师?”
66 |
67 | “你知道吗,不管上游的水势多么凶猛,从大坝出来以后总是安全而稳定。”S君并没有回答我的问题,而是自顾自地说道。
68 |
69 | “原来你开始用Kafka。不错,孺子可教。”
70 |
71 | S君吐了一下舌头:“还是师傅教导有方。”
72 |
73 | 前不久,S君的爬虫刚刚达到了日产数据千万条的目标。然而他只高兴了一天。因为他发现,数据写到数据库以后,读起来很麻烦。
74 |
75 | S君有多个数据分析的系统需要从数据库里面读取爬虫爬好的数据,但是从每天千万量级的数据中寻找特定的数据是一个很慢的过程。如果程序遇到异常导致崩溃,又得从头开始读。
76 |
77 | S君问我:“现在我每一个数据分析的脚本都要从数据库里面读一次数据,做了太多重复的工作,单机单节点的数据库快要撑不住了。我是不是要去学习分库分表搭建集群啊?”
78 |
79 | 我告诉S君:“这个后面你自然是需要去做的。但现在,你可以先试一试Kafka,我已经搭建好了一个Kafka的集群了,你这样使用……”。
80 |
81 | 后来,S君让所有爬虫把爬到的数据到直接送进了Kafka,然后再从Kafka里面读数据出来,一个Group用来备份原始数据,一份Group用来生成中间表,一份Group用来监控报警,一份Group用来绘制DashBoard。无论爬虫塞给Kafka的数据有多少,有多快,从Kafka读数据的地方都能按照自己的节奏来消费和使用。
82 |
83 | ## 既然收集了数据就要让它发光发热
84 |
85 | S君在加拿大留学时学的专业是金融数学和统计。所以他对数据分析也很有兴趣。在他爬虫收集的数据够用以后,我跟他讲了如何使用Pandas来分析数据。
86 |
87 | S君把他分析的酒店价格变化数据给分享给了我们。不愧是金融+数学+统计学背景的高级知识分子 + 超级强大的Pandas + 超级好用的Jupyter。这份数据不仅完美再现了过去一年的价格走势,还预测了未来的任何变化,多达四十六张图表似乎穷尽了所有的组合。
88 |
89 | ## 草木竹石皆可破敌
90 |
91 | S君曾经遇到过一个特别简单的电商网站。页面几乎像素级抄袭淘宝,但是完全没有任何反爬虫的机制。以S君的水平,从审查元素,到开发完成,仅仅用了半个小时。爬虫安全平稳又顺利地运行了三个星期。
92 |
93 | 然后,有一天早上,爬虫死掉了。
94 |
95 | S君用尽毕生所学,无法再从这个网站上爬到任何有价值的信息。这个网站似乎请来了一个机器行为对抗的大神级人物。人用浏览器一点问题都没有,但S君的任何隐藏爬虫的手段都被轻易识破。
96 |
97 | S君找到我:“师傅,这个网站我搞不定。”
98 |
99 | “你能搞定。动动脑子。”
100 |
101 | “我会的所有技术都用上了,完全看不出破解他反爬虫机制的方法。”S君已经失去了信心。
102 |
103 | “那就,不要用技术去对抗。用你的脑子。”
104 |
105 | S君抱着显示器用头一遍一遍的撞。
106 |
107 | 我问S君:“你有没有思考一个问题,这个网站模仿了淘宝的皮,却又毫无反爬虫机制。你觉得他的老板是一个什么样的人?你听过那个段子吗?”
108 |
109 | S君突然一跃而起:“我给你一万元,你帮我做一个网站吧。你想要什么样的网站?很简单,就淘宝那样的。你是说这个段子吗?”
110 |
111 | “对。”
112 |
113 | S君突然之间荣光焕发:“有办法了!”
114 |
115 | 只见S君重新在浏览器打开了这个网站,找到了客户服务热线。电话一拨通他就开始一通污言秽语骂起来:“……你们网站到底在搞什么?为什么今天一会能登录一会不能登录?找你们老板来!我来教他怎么做网站!……”
116 |
117 | 半小时以后,网站反爬虫机制全部解除。
118 |
119 | 此刻,S君面向西面双手合十,自言自语:“兄弟,对不起了,只有让你来背这个锅了。”
120 |
121 |
122 | ## 你小学上课传过纸条吗
123 |
124 | “我现在能体会那些半路拦截纸条的人是什么心态了。”这是S君第一次使用Charles时对我说的话。
125 |
126 | 从此以后,我很少看到S君分析网页了。因为他学会了在爬虫开发的过程中,首先通过中间人攻击技术分析微信小程序和手机App。这种方式往往能够直接获得数据,拿到数据以后就能直接储存,再也不用写烦人的XPath或者长的跟表情符号一样的正则表达式了。
127 |
128 | 有一天,我在玩一个网页版的黑客解密游戏,在网页上寻找某个地方隐藏起来的密码,然后输入每一关的回答框中,答对才能进入下一关。
129 |
130 | 游戏有12关,而我卡在了第6关。只见S君拿着电脑走到我面前,指着第12关的通关页面跟我炫耀。
131 |
132 | “你是不是用MITMProxy替换了这个网站的Js文件?”
133 |
134 | “果然还是瞒不过师傅你啊。”
135 |
136 | “你拦截了别人的纸条,做了修改,然后又叠好继续传下去,你有考虑过发纸条的人和收纸条的人的感受吗?”
137 |
138 | “我小学时候不传纸条,都是妹子直接约我的。”
139 |
140 | ## 加密?不存在的
141 |
142 | “前端没有秘密”。S君在成功逆向了一个网站的Js文件以后如是对我说。
143 |
144 | “那是因为这个网站的Js代码就赤裸裸地放在你面前,完全没有混淆。”我对S君说道。
145 |
146 | “不怕,我可以用Node.js来运行混淆过的代码。我已经搭建好Node.js服务了,只要把Js代码传进去,他就会把结果给我返回回来。”S君对此似乎一脸自行。
147 |
148 | “你什么时候学会的Node.js?”
149 |
150 | “这不是师傅你说过技多不压身吗?既然做爬虫需要动JavaScript,那我顺手就把Node.js给学了。”S君毫不畏惧的表情,似乎证明他已经猜透了我要问什么。
151 |
152 | “那如果目标没有网站,只有App呢?”
153 |
154 | “不怕,Android 逆向工程我也顺便研究了一点。Java我也看得懂。”
155 |
156 | “看来这些已经不需要我再教你了。”
157 |
158 | ## 我一根指头就能捏死你,但我不想伤害你
159 |
160 | S君有一天问我:“假设你现在在小学课堂上,前面的同学让你把纸条传给后面的女生,你会怎么做?”
161 |
162 | 我说:“查看复制/修改删除/拦截丢弃”。
163 |
164 | S君嘿嘿一笑:“比如说,前后三次的纸条分别为‘听说你奶奶生病了,我们周末一起去看望她吧‘,’今晚我爸妈不在,去我家玩吗?’, ‘我刚拿到这个月压岁钱,老师一下课我们就去吃好吃的。’”
165 |
166 | 我说:“女孩漂亮的话,我改一下第二张纸条,改成‘今晚我爸妈不在,我们一起去青南家玩吗?’”。
167 |
168 | S君露出了嫌弃的眼神:“师傅,你可是说过你最讨厌技术含量低的事情啊,你涂改了纸条,别人不会发现?你笔迹都不一样啊!”。
169 |
170 | 我问S君:“那你有何高见?”
171 |
172 | S君抬头仰望这窗外的天空:“如果是我,那么我会临摹第一张纸条上面的`生病了` `去看望` `她` `我`,第二张纸条上面的`爸妈`,第三张纸条上面的`拿` `钱` `老师`这些字的笔迹。然后改换一下顺序就变成了:`爸妈,我老师生病了,我拿钱去看望她`。最后我把这张伪造的纸条拿去找写纸条的那个同学他爸妈要钱。”
173 |
174 | “我猜,你想用中间人攻击截取别人的Cookies,然后用这些Cookies偷偷登录网站,进行你的不可告人的目的。”
175 |
176 | S君笑道:“哈哈哈,我想想都害怕。但是每当我想到,我拥有一种可怕的力量,而我还能控制住这种力量。我就知道我和街上的普通人不一样了。”
177 |
178 | ## 你肯定薅了直播答题的羊毛吧
179 |
180 | 去年年底的直播答题着实火了一把。那个时候,我和S君分开已经有一段时间了。我相信,在全民答题的每一个夜晚,S君的电脑上一定连着不少于六台安卓手机。这些手机运行着不同的答题平台,能够自动读取屏幕上的问题并自动选择答案。
181 |
182 | 我把安卓自动化测试技术教给S君,本来是让他结合爬虫,实现群控从而抓取一些难以处理的数据,但我相信他肯定会用来答题。
183 |
184 | 变通,这一点他学的越来越好了。
185 |
186 | 只希望他不要成为羊毛党。
187 |
188 | ## 后记
189 |
190 | 后来,我再也没有见过S君这样有趣的人。所以我把我教给S君的东西,写成了一本书:《Python爬虫开发 从入门到实战》,现在已在京东,当当与亚马逊上架。希望你也能变成S君一样有趣而又厉害的人。
191 |
192 | 爬虫是一门杂学。因为在一个完整的开发过程中,需要涉及到的知识可以包括但不限于:Python,HTML,JavaScript,正则表达式,XPath,数据库,Redis,消息队列,Docker,ELK,Hadoop,数据分析,ETL,中间人攻击,自动化测试技术,可视化……
193 |
194 | 这其中的任何一项,在一个大公司里面都可以让很多人来做。
195 |
196 | 爬虫开发,就像这篇文章里面反复出现的一个词:变通——只要能够获得数据,任何技术都可以使用。所谓草木竹石皆可为剑。爬虫不应该是一个枯燥的一成不变的模式化的工作。而是一个充满了创意和挑战,能够让旁观者大呼“我X还能这样搞”的工作。
197 |
198 | 爬虫开发,绝对不仅仅是Scrapy,PySpider,requests这些框架或者库的使用。所以在这本书里面,我也刻意减少了框架使用说明的部分,而把重点放在了各种突破反爬虫机制或者使用变通的方法绕过反爬虫机制的方法论和实践中。
199 |
200 | 通过学习爬虫,你最后不一定选择爬虫工程师这个岗位,但是在学习爬虫的过程中,你将会接触到的各种工具,方法,服务组件,都会在你以后的生活和工作中帮到你,让你知道,在遇到一个问题的时候,解决方法在哪个地方。
201 |
202 | 最后,我自己,我在网络上的名字叫做青南或者kingname(青南的音译)。2014年加入LCTT,2015年作为极客学院爬虫课程讲师,开设的爬虫开发课程,受众超过10万。现在在网易游戏担任高级数据挖掘工程师。
203 |
204 |
--------------------------------------------------------------------------------
/etc/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM selenium/standalone-chrome:latest
2 | LABEL mantainer="contact@kingname.info"
3 |
4 | USER root
5 | ENV PATH /usr/local/bin:$PATH
6 |
7 | ENV LANG en_US.UTF-8
8 | ENV PYTHONUNBUFFERED=0
9 | # runtime dependencies
10 | RUN apt-get update && apt-get install -y --no-install-recommends \
11 | tcl \
12 | tk \
13 | && rm -rf /var/lib/apt/lists/*
14 |
15 |
16 | RUN apt-get update \
17 | && apt-get -y install ttf-wqy-microhei ttf-wqy-zenhei \
18 | && apt-get clean
19 |
20 | RUN apt-get update && apt-get install -y build-essential
21 | RUN apt-get update && apt-get install -y python3 python3-dev python3-pip python3-setuptools
22 | #ENV DEBIAN_FRONTEND noninteractive
23 | RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
24 | # Setup and install base system software
25 | RUN echo "locales locales/locales_to_be_generated multiselect en_US.UTF-8 UTF-8" | debconf-set-selections \
26 | && echo "locales locales/default_environment_locale select en_US.UTF-8" | debconf-set-selections \
27 | && apt-get update \
28 | && apt-get --yes --no-install-recommends install \
29 | locales tzdata ca-certificates sudo \
30 | bash-completion iproute2 curl xvfb chromium-browser wget vim libmysqlclient-dev\
31 | && rm -rf /var/lib/apt/lists/*
32 | ENV LANG en_US.UTF-8
33 |
34 |
35 | # Install Python stack
36 | RUN apt-get update \
37 | && apt-get --yes --no-install-recommends install libxml2-dev libxslt1-dev zlib1g-dev libffi-dev libssl-dev vim\
38 | software-properties-common \
39 | && rm -rf /var/lib/apt/lists/*
40 |
41 |
42 | # Install Python modules
43 | RUN python3 -m pip install -i http://pypi.douban.com/simple/ --trusted-host=pypi.douban.com pyvirtualdisplay selenium redis requests
44 | COPY test.py test.py
45 | cmd python test.py
46 |
--------------------------------------------------------------------------------
/勘误表.md:
--------------------------------------------------------------------------------
1 | # 勘误表
2 |
3 | 这里将会记录读者返回的印刷错误和改正方法。
4 |
5 | ## 16页
6 |
7 | ### 第4行
8 |
9 | ```python
10 | tuple_2 = ('哈哈哈哈', '嘿嘿嘿嘿']
11 | ```
12 |
13 | 应改为:
14 |
15 | ```python
16 | tuple_2 = ('哈哈哈哈', '嘿嘿嘿嘿')
17 | ```
18 |
19 | ## 55页
20 |
21 | ### 代码段第3行
22 |
23 | ```python
24 | source = requests.get('https://www.baidu.com').content.deocde()
25 | ```
26 |
27 | 应改为
28 |
29 | ```python
30 | source = requests.get('https://www.baidu.com').content.decode()
31 | ```
32 |
33 | ## 73页
34 |
35 | ### 第2行,
36 |
37 | ```python
38 | 输入mport lxml
39 | ```
40 |
41 | 应改为
42 |
43 | ```python
44 | 输入import lxml
45 | ```
46 |
47 | ### 倒数第5,6行(本页最后一个代码块第1,2行)
48 |
49 | ```python
50 | import lxml html
51 | ```
52 |
53 | 应改为
54 |
55 | ```python
56 | import lxml.html
57 | selector = lxml.html.fromstring('网页源代码')
58 | ```
59 |
60 | 以图片为准。
61 |
62 | ## 95页
63 |
64 | ### 第一个代码块最后一行
65 |
66 | ```
67 | collection = db1['spider']
68 | ```
69 |
70 | 应改为:
71 |
72 | ```
73 | collection = database['spider']
74 | ```
75 |
76 | ### 第3个代码块倒数第二行
77 |
78 | ```
79 | collection = db.test
80 | ```
81 |
82 | 应改为:
83 |
84 | ```
85 | collection = database.test
86 | ```
87 |
88 | ### 第4个代码块最后一行
89 |
90 | ```
91 | collection = db1['spider']
92 | ```
93 |
94 | 应改为:
95 |
96 | ```
97 | collection = database['spider']
98 | ```
99 |
100 | ### 第5个代码块最后一行
101 |
102 | ```
103 | collection = db1['spider']
104 | ```
105 |
106 | 应改为:
107 |
108 | ```
109 | collection = database['spider']
110 | ```
111 |
112 | ## 176 页
113 |
114 | ### 第2个代码块
115 |
116 | ```
117 | --proxy_host=代理IP
118 | ```
119 |
120 | 应改为:
121 |
122 | ```
123 | --proxy-host=代理IP
124 | ```
125 |
126 | ## 183 页
127 |
128 | ### 第3个代码块
129 |
130 | ```
131 | device(scrollable=True).scroll.vert.forward()
132 | ```
133 |
134 | 应改为:
135 |
136 | ```
137 | device(scrollable=True).scroll.vert.backward()
138 | ```
139 |
140 | ## 190 页
141 |
142 | ### 第1段第1行
143 |
144 | ```
145 | 在终端输入 crontab-e
146 | ```
147 |
148 | 应改为:
149 |
150 | ```
151 | 在终端输入 crontab -e
152 | ```
153 |
154 | ## 193 页
155 |
156 | ### 第1个代码块
157 |
158 | ```python
159 | while True:
160 | for phone in phone_list:
161 | if phone.is_alive():
162 | break
163 | else:
164 | break
165 | time.sleep(60)
166 | ```
167 |
168 | 应改为:
169 |
170 | ```python
171 | while True:
172 | for phone in phone_list:
173 | if phone.is_alive():
174 | break
175 | else:
176 | break
177 | time.sleep(60)
178 | ```
179 |
180 | 即最后一行应缩进一个级别,使其位于 `while` 循环内
181 |
182 |
183 | ## 242页
184 |
185 | ### 12.1.2 小节第二行
186 |
187 | ```
188 | 爬虫中间键的作用对象是爬虫
189 | ```
190 |
191 | 应改为:
192 |
193 | ```
194 | 爬虫中间件的作用对象是爬虫
195 | ```
196 |
--------------------------------------------------------------------------------
/第10章/program/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/第10章/program/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | $USER_HOME$/.subversion
6 | true
7 |
8 |
--------------------------------------------------------------------------------
/第10章/program/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/第10章/program/.idea/program.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/第10章/program/example_automatic_wechat.py:
--------------------------------------------------------------------------------
1 | #! /Users/kingname/Project/scrapy_venv/bin/python
2 |
3 | from uiautomator import Device
4 |
5 | device = Device()
6 | device.wakeup()
7 |
8 | wechat_icon = device(text='微信')
9 | if not wechat_icon.exists:
10 | device.swipe(400, 600, 0, 600)
11 | if wechat_icon.exists:
12 | wechat_icon.click()
13 |
14 | device(scrollable=True).scroll.vert.toBeginning()
15 | while True:
16 | girl_friend = device(text='女朋友')
17 | if girl_friend.exists:
18 | girl_friend.click()
19 | break
20 | else:
21 | device(scrollable=True).scroll.vert.forward()
22 |
23 | device(className="android.widget.EditText").set_text('早上好,本消息为自动发送。')
24 | device.press.back()
25 | send_button = device(text="发送")
26 | if send_button.exists:
27 | send_button.click()
28 |
--------------------------------------------------------------------------------
/第10章/program/example_launch_wechat.py:
--------------------------------------------------------------------------------
1 | from uiautomator import Device
2 |
3 | device = Device()
4 | input_box = device(resourceId='com.taptap:id/input_box')
5 | input_box.set_text('123')
6 |
7 |
8 | # device.watcher('In_Detail_to_Search').when(text='我的世界 Minecraft').when(text='预约').press.back()
9 |
10 | # input_box = device(resourceId='com.taptap:id/input_box')
11 | # input_box.clear_text()
12 | # input_box.set_text('汉家江湖')
13 |
14 | # input_box = device(resourceId='com.taptap:id/input_box')
15 | # if input_box.exists:
16 | # input_box.set_text('汉家江湖')
17 | # else:
18 | # print('搜索框不存在')
19 |
20 | # if device.screen == 'on':
21 | # print('当前手机屏幕为点亮状态')
22 | # device.press.power()
23 | # elif device.screen == 'off':
24 | # print('当前手机屏幕为关闭状态')
25 | # device.press.power()
26 |
27 | # device.wakeup() # 点亮屏幕
28 | # device.sleep() # 关闭屏幕
29 |
30 |
31 | # for i in range(20):
32 | # game_title_list = device(resourceId='com.taptap:id/bottom_app_name')
33 |
34 | # for title in game_title_list:
35 | # print(title.text)
36 | # device(scrollable=True).scroll.vert.forward()
37 |
38 | # print(device.dump())
39 |
40 | # device(packageName='com.android.systemui')
41 | # device(className='android.widget.FrameLayout')
42 | # device(resourceId="com.android.systemui:id/clock")
43 | # device(index="3", resourceId="com.android.systemui:id/mobile_combo")
44 |
45 | # device(text='微信').click()
--------------------------------------------------------------------------------
/第10章/program/example_multi_device.py:
--------------------------------------------------------------------------------
1 | import time
2 | import random
3 | import threading
4 |
5 |
6 | class PhoneThread(threading.Thread):
7 | def __init__(self):
8 | threading.Thread.__init__(self)
9 |
10 | def run(self):
11 | time.sleep(random.randint(10, 500))
12 |
13 |
14 | phone_list = []
15 | for i in range(10):
16 | phone = PhoneThread()
17 | phone.start()
18 | phone_list.append(phone)
19 |
20 | while True:
21 | for phone in phone_list:
22 | if phone.is_alive():
23 | break
24 | else:
25 | break
26 | time.sleep(60)
27 |
28 |
29 |
30 | # phone = PhoneThread()
31 | # phone.start()
32 | # print(f'目前子线程实例是否还活着:{phone.is_alive()}')
33 | # print('主线程等5秒')
34 | # time.sleep(5)
35 | # print(f'现在子线程实例是否还活着:{phone.is_alive()}')
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/第10章/program/example_taptap.py:
--------------------------------------------------------------------------------
1 | import time
2 | import redis
3 | import threading
4 | from uiautomator import Device
5 |
6 |
7 | class PhoneThread(threading.Thread):
8 | def __init__(self, serial):
9 | threading.Thread.__init__(self)
10 | self.serial = serial
11 | self.device = Device(serial)
12 | self.client = redis.StrictRedis()
13 |
14 | def run(self):
15 | while self.client.llen('game_name') != 0:
16 | game_name = self.client.lpop('game_name')
17 | if game_name:
18 | game_name = game_name.decode()
19 | self.crawl(game_name)
20 |
21 | def crawl(self, game_name):
22 | input_box = self.device(resourceId='com.taptap:id/input_box')
23 | input_box.clear_text()
24 | input_box.set_text(game_name)
25 | self.device(resourceId="com.taptap:id/search_btn").click()
26 | search_result = self.device(textContains=game_name, resourceId="com.taptap:id/app_title")
27 | if search_result.wait.exists(timeout=3000):
28 | search_result.click()
29 | else:
30 | self.crawl(game_name)
31 | return
32 | download_count = self.device(resourceId="com.taptap:id/download_count")
33 | if download_count.wait.exists(timeout=3000):
34 | print(game_name, download_count.text)
35 | self.device.press.back()
36 |
37 |
38 | client = redis.StrictRedis()
39 | with open('target', encoding='utf-8') as f:
40 | game_list = [x.strip() for x in f.readlines()]
41 | with open('phone_list', encoding='utf-8') as f:
42 | serial_list = [x.strip() for x in f.readlines()]
43 |
44 | for game in game_list:
45 | client.lpush('game_name', game)
46 |
47 | phone_list = []
48 | for serial in serial_list:
49 | phone_thread = PhoneThread(serial)
50 | phone_thread.start()
51 | phone_list.append(phone_thread)
52 |
53 | while phone_list:
54 | phone_list = [x for x in phone_list if x.is_alive()]
55 | time.sleep(5)
56 |
57 |
--------------------------------------------------------------------------------
/第10章/program/homework_boss.py:
--------------------------------------------------------------------------------
1 | from uiautomator import Device
2 |
3 | device = Device()
4 | resource_id_dict = {
5 | 'salary': 'com.hpbr.bosszhipin:id/tv_position_salary',
6 | 'company': 'com.hpbr.bosszhipin:id/tv_company_name',
7 | 'address': 'com.hpbr.bosszhipin:id/tv_location',
8 | 'experence': 'com.hpbr.bosszhipin:id/tv_work_exp',
9 | 'degree': 'com.hpbr.bosszhipin:id/tv_degree'}
10 |
11 |
12 | def crawl():
13 | for job in device(resourceId='com.hpbr.bosszhipin:id/rl_section_1'):
14 | result_dict = {}
15 | job_info_box = job.child(resourceId='com.hpbr.bosszhipin:id/ll_position')
16 | job_name = job_info_box.child(resourceId='com.hpbr.bosszhipin:id/tv_position_name')
17 | if not job_name.exists:
18 | return
19 | result_dict['job_name'] = job_name.text
20 | for key, resource_id in resource_id_dict.items():
21 | value = job.child(resourceId=resource_id)
22 | if not value.exists:
23 | return
24 | result_dict[key] = value.text
25 | print(result_dict)
26 |
27 |
28 | def scroll():
29 | device(scrollable=True).scroll.vert.forward()
30 |
31 | if __name__ == '__main__':
32 | while True:
33 | crawl()
34 | scroll()
35 |
--------------------------------------------------------------------------------
/第10章/program/phone_list:
--------------------------------------------------------------------------------
1 | 76ef903a7ce4
2 | a534fc23d
3 | 23451aef12
4 | 7f1a4ccee8
--------------------------------------------------------------------------------
/第10章/program/target:
--------------------------------------------------------------------------------
1 | 漂流少女
2 | 最囧游戏2
3 | 地球末日
4 | 平衡城市
5 | 超次元坦克
6 | 驾驶学校 2017
7 | 野蛮人大作战
8 | 孤狼
9 | 众生
10 | 蛇它虫
11 | 王者军团
12 | 原谅帽大作战
13 | yellow
14 | 崩坏3
15 | 元气骑士
16 | 小米超神
17 | 王者荣耀
18 | 炉石传说
19 | 像素大逃杀
20 | 虚拟乒乓球
--------------------------------------------------------------------------------
/第11章/program/.idea/baidu.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/第11章/program/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/第11章/program/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/第11章/program/Pipfile:
--------------------------------------------------------------------------------
1 | [[source]]
2 |
3 | url = "https://pypi.python.org/simple"
4 | verify_ssl = true
5 | name = "pypi"
6 |
7 |
8 | [packages]
9 |
10 | scrapy = "*"
11 | pymongo = "*"
12 | scrapy-redis = "*"
13 |
14 |
15 | [dev-packages]
16 |
17 |
--------------------------------------------------------------------------------
/第11章/program/baidu/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingname/SourceCodeOfBook/ab7275108994dca564905818b678bbd2f771c18e/第11章/program/baidu/__init__.py
--------------------------------------------------------------------------------
/第11章/program/baidu/items.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Define here the models for your scraped items
4 | #
5 | # See documentation in:
6 | # http://doc.scrapy.org/en/latest/topics/items.html
7 |
8 | import scrapy
9 |
10 |
11 | class BaiduItem(scrapy.Item):
12 | # define the fields for your item here like:
13 | name = scrapy.Field()
14 | price = scrapy.Field()
15 | size = scrapy.Field()
16 | comments = scrapy.Field()
17 |
18 |
19 | class PersonInfoItem(scrapy.Item):
20 | name = scrapy.Field()
21 | age = scrapy.Field()
22 | salary = scrapy.Field()
23 | phone = scrapy.Field()
24 |
25 |
26 | class BlogItem(scrapy.Item):
27 | title = scrapy.Field()
28 | url = scrapy.Field()
29 | post_time = scrapy.Field()
30 | category = scrapy.Field()
31 | detail = scrapy.Field()
32 |
--------------------------------------------------------------------------------
/第11章/program/baidu/pipelines.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Define your item pipelines here
4 | #
5 | # Don't forget to add your pipeline to the ITEM_PIPELINES setting
6 | # See: http://doc.scrapy.org/en/latest/topics/item-pipeline.html
7 |
8 | import pymongo
9 | from scrapy.conf import settings
10 |
11 |
12 | class BaiduPipeline(object):
13 | def __init__(self):
14 | host = settings['MONGODB_HOST']
15 | port = settings['MONGODB_PORT']
16 | db_name = settings['MONGODB_DBNAME']
17 | client = pymongo.MongoClient(host=host, port=port)
18 | db = client[db_name]
19 | self.post = db[settings['MONGODB_DOCNAME']]
20 |
21 | def process_item(self, item, spider):
22 | person_info = dict(item)
23 | self.post.insert(person_info)
24 | return item
25 |
--------------------------------------------------------------------------------
/第11章/program/baidu/settings.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Scrapy settings for baidu project
4 | #
5 | # For simplicity, this file contains only settings considered important or
6 | # commonly used. You can find more settings consulting the documentation:
7 | #
8 | # http://doc.scrapy.org/en/latest/topics/settings.html
9 | # http://scrapy.readthedocs.org/en/latest/topics/downloader-middleware.html
10 | # http://scrapy.readthedocs.org/en/latest/topics/spider-middleware.html
11 |
12 | BOT_NAME = 'baidu'
13 |
14 | SPIDER_MODULES = ['baidu.spiders']
15 | NEWSPIDER_MODULE = 'baidu.spiders'
16 |
17 |
18 | # Crawl responsibly by identifying yourself (and your website) on the user-agent
19 | #USER_AGENT = 'baidu (+http://www.yourdomain.com)'
20 |
21 | # Obey robots.txt rules
22 | ROBOTSTXT_OBEY = False
23 |
24 | # Configure maximum concurrent requests performed by Scrapy (default: 16)
25 | #CONCURRENT_REQUESTS = 32
26 |
27 | # Configure a delay for requests for the same website (default: 0)
28 | # See http://scrapy.readthedocs.org/en/latest/topics/settings.html#download-delay
29 | # See also autothrottle settings and docs
30 | #DOWNLOAD_DELAY = 3
31 | # The download delay setting will honor only one of:
32 | #CONCURRENT_REQUESTS_PER_DOMAIN = 16
33 | #CONCURRENT_REQUESTS_PER_IP = 16
34 |
35 | # Disable cookies (enabled by default)
36 | #COOKIES_ENABLED = False
37 |
38 | # Disable Telnet Console (enabled by default)
39 | #TELNETCONSOLE_ENABLED = False
40 |
41 | # Override the default request headers:
42 | #DEFAULT_REQUEST_HEADERS = {
43 | # 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
44 | # 'Accept-Language': 'en',
45 | #}
46 |
47 | # Enable or disable spider middlewares
48 | # See http://scrapy.readthedocs.org/en/latest/topics/spider-middleware.html
49 | #SPIDER_MIDDLEWARES = {
50 | # 'baidu.middlewares.MyCustomSpiderMiddleware': 543,
51 | #}
52 |
53 | # Enable or disable downloader middlewares
54 | # See http://scrapy.readthedocs.org/en/latest/topics/downloader-middleware.html
55 | #DOWNLOADER_MIDDLEWARES = {
56 | # 'baidu.middlewares.MyCustomDownloaderMiddleware': 543,
57 | #}
58 |
59 | # Enable or disable extensions
60 | # See http://scrapy.readthedocs.org/en/latest/topics/extensions.html
61 | #EXTENSIONS = {
62 | # 'scrapy.extensions.telnet.TelnetConsole': None,
63 | #}
64 |
65 | # Configure item pipelines
66 | # See http://scrapy.readthedocs.org/en/latest/topics/item-pipeline.html
67 | ITEM_PIPELINES = {
68 | 'baidu.pipelines.BaiduPipeline': 300,
69 | }
70 |
71 | # Enable and configure the AutoThrottle extension (disabled by default)
72 | # See http://doc.scrapy.org/en/latest/topics/autothrottle.html
73 | #AUTOTHROTTLE_ENABLED = True
74 | # The initial download delay
75 | #AUTOTHROTTLE_START_DELAY = 5
76 | # The maximum download delay to be set in case of high latencies
77 | #AUTOTHROTTLE_MAX_DELAY = 60
78 | # The average number of requests Scrapy should be sending in parallel to
79 | # each remote server
80 | #AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0
81 | # Enable showing throttling stats for every response received:
82 | #AUTOTHROTTLE_DEBUG = False
83 |
84 | # Enable and configure HTTP caching (disabled by default)
85 | # See http://scrapy.readthedocs.org/en/latest/topics/downloader-middleware.html#httpcache-middleware-settings
86 | #HTTPCACHE_ENABLED = True
87 | #HTTPCACHE_EXPIRATION_SECS = 0
88 | #HTTPCACHE_DIR = 'httpcache'
89 | #HTTPCACHE_IGNORE_HTTP_CODES = []
90 | #HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'
91 |
92 |
93 | SCHEDULER = "scrapy_redis.scheduler.Scheduler"
94 | DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
95 | SCHEDULER_PERSIST = True
96 | SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderPriorityQueue"
97 |
98 |
99 | HEADERS = {
100 | 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
101 | 'accept-encoding': 'gzip, deflate, br',
102 | 'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8',
103 | 'cache-control': 'max-age=0',
104 | 'dnt': '1',
105 | 'upgrade-insecure-requests': '1',
106 | 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
107 | }
108 |
109 | MONGODB_HOST = '127.0.0.1'
110 | MONGODB_PORT = 27017
111 | MONGODB_DBNAME = 'blogspider'
112 | MONGODB_DOCNAME = 'blog'
113 |
114 | REDIS_HOST= '127.0.0.1'
115 | REDIS_PORT = 6379
116 |
--------------------------------------------------------------------------------
/第11章/program/baidu/spiders/BlogSpider.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import scrapy
3 | from scrapy_redis.spiders import RedisSpider
4 | from baidu.items import BlogItem
5 | from lxml import etree
6 | from html import unescape
7 |
8 |
9 | class BlogSpider(RedisSpider):
10 | name = "BlogSpider"
11 | allowed_domains = ["www.kingname.info"]
12 | redis_key = 'blogspider'
13 | start_urls = ['https://www.kingname.info/archives/']
14 | host = 'https://www.kingname.info'
15 |
16 | def parse(self, response):
17 | title_tag_list = response.xpath('//a[@class="post-title-link"]')
18 | for title_tag in title_tag_list:
19 | article_title = title_tag.xpath('span/text()').extract_first()
20 | article_url = self.host + title_tag.xpath('@href').extract_first()
21 | item = BlogItem()
22 | item['title'] = article_title
23 | item['url'] = article_url
24 | yield scrapy.Request(article_url,
25 | headers=self.settings['HEADERS'],
26 | callback=self.parse_detail,
27 | meta={'item': item})
28 |
29 | def parse_detail(self, response):
30 | item = response.meta['item']
31 | post_time = response.xpath('//time[@title="Post created"]/@datetime').extract_first()
32 | category = response.xpath('//span[@itemprop="about"]/a/span/text()').extract_first()
33 | post_body = response.xpath('//div[@class="post-body"]')
34 | body_html = unescape(etree.tostring(post_body[0]._root).decode())
35 | item['post_time'] = post_time
36 | item['category'] = category
37 | item['detail'] = body_html
38 | yield item
39 |
--------------------------------------------------------------------------------
/第11章/program/baidu/spiders/__init__.py:
--------------------------------------------------------------------------------
1 | # This package will contain the spiders of your Scrapy project
2 | #
3 | # Please refer to the documentation for information on how to create and manage
4 | # your spiders.
5 |
--------------------------------------------------------------------------------
/第11章/program/baidu/spiders/example.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import scrapy
3 |
4 |
5 | class ExampleSpider(scrapy.Spider):
6 | name = "example"
7 | allowed_domains = ["baidu.com"]
8 | start_urls = ['http://baidu.com/']
9 |
10 | def parse(self, response):
11 | # title = response.xpath('//title/text()').extract()
12 | # search_button_text = response.xpath('//input[@class="bg s_btn"]/@value').extract()
13 | # print(title)
14 | # print(search_button_text)
15 |
16 | title_example = response.xpath('//title/text()')
17 | title_example_1 = title_example.extract()[0]
18 | title_example_2 = title_example[0].extract()
19 | print('先读取下标为0的元素,再extract: {}'.format(title_example_1))
20 | print('先extract,再读取下标为0的元素: {}'.format(title_example_2))
21 |
22 |
--------------------------------------------------------------------------------
/第11章/program/baidu/spiders/exercise11_1.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import scrapy
3 |
4 |
5 | class Exercise111Spider(scrapy.Spider):
6 | name = "exercise11_1"
7 | allowed_domains = ["exercise.kingname.info"]
8 | start_urls = ['http://exercise.kingname.info/exercise_xpath_1.html']
9 |
10 | def parse(self, response):
11 | name_list = response.xpath('//li[@class="name"]/text()').extract()
12 | price_list = response.xpath('//li[@class="price"]/text()').extract()
13 | for i in range(len(name_list)):
14 | print('商品:{}, 价格为:{}'.format(name_list[i], price_list[i]))
15 |
--------------------------------------------------------------------------------
/第11章/program/baidu/spiders/exercise11_2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import scrapy
3 |
4 |
5 | class Exercise112Spider(scrapy.Spider):
6 | name = "exercise11_2"
7 | allowed_domains = ["exercise.kingname.info"]
8 | start_urls = ['http://exercise.kingname.info/exercise_xpath_2.html']
9 |
10 | def parse(self, response):
11 | item_list = response.xpath('//ul[@class="item"]')
12 | for item in item_list:
13 | name = item.xpath('li[@class="name"]/text()').extract()
14 | price = item.xpath('li[@class="price"]/text()').extract()
15 | name = name[0] if name else 'N/A'
16 | price = price[0] if price else 'N/A'
17 | print('商品:{}, 价格为:{}'.format(name, price))
18 |
--------------------------------------------------------------------------------
/第11章/program/baidu/spiders/exercise11_3.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import scrapy
3 | from baidu.items import PersonInfoItem
4 |
5 | class Exercise113Spider(scrapy.Spider):
6 | name = "exercise11_3"
7 | allowed_domains = ["exercise.kingname.info"]
8 | start_urls = ['http://exercise.kingname.info/exercise_xpath_3.html']
9 |
10 | def parse(self, response):
11 | person_list = response.xpath('//div[@class="person_table"]/table/tbody/tr')
12 | for person in person_list:
13 | item = PersonInfoItem()
14 | person_info = person.xpath('td/text()').extract()
15 | item['name'] = person_info[0]
16 | item['age'] = person_info[1]
17 | item['salary'] = person_info[2]
18 | item['phone'] = person_info[3]
19 | yield item
20 |
--------------------------------------------------------------------------------
/第11章/program/main.py:
--------------------------------------------------------------------------------
1 | from scrapy import cmdline
2 | cmdline.execute("scrapy crawl BlogSpider".split())
3 |
--------------------------------------------------------------------------------
/第11章/program/scrapy.cfg:
--------------------------------------------------------------------------------
1 | # Automatically created by: scrapy startproject
2 | #
3 | # For more information about the [deploy] section see:
4 | # https://scrapyd.readthedocs.org/en/latest/deploy.html
5 |
6 | [settings]
7 | default = baidu.settings
8 |
9 | [deploy]
10 | #url = http://localhost:6800/
11 | project = baidu
12 |
--------------------------------------------------------------------------------
/第12章/program/AdvanceSpider/.idea/AdvanceSpider.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/第12章/program/AdvanceSpider/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/第12章/program/AdvanceSpider/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | $USER_HOME$/.subversion
6 | true
7 |
8 |
--------------------------------------------------------------------------------
/第12章/program/AdvanceSpider/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/第12章/program/AdvanceSpider/AdvanceSpider/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingname/SourceCodeOfBook/ab7275108994dca564905818b678bbd2f771c18e/第12章/program/AdvanceSpider/AdvanceSpider/__init__.py
--------------------------------------------------------------------------------
/第12章/program/AdvanceSpider/AdvanceSpider/items.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Define here the models for your scraped items
4 | #
5 | # See documentation in:
6 | # http://doc.scrapy.org/en/latest/topics/items.html
7 |
8 | import scrapy
9 |
10 |
11 | class ErrorItem(scrapy.Item):
12 | # define the fields for your item here like:
13 | # name = scrapy.Field()
14 | page = scrapy.Field()
15 | error_time = scrapy.Field()
16 |
--------------------------------------------------------------------------------
/第12章/program/AdvanceSpider/AdvanceSpider/middlewares.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Define here the models for your spider middleware
4 | #
5 | # See documentation in:
6 | # http://doc.scrapy.org/en/latest/topics/spider-middleware.html
7 |
8 | import json
9 | import time
10 | import redis
11 | import random
12 | import datetime
13 | from AdvanceSpider.items import ErrorItem
14 | from scrapy import signals
15 | from selenium import webdriver
16 | from scrapy.conf import settings
17 | from scrapy.http import HtmlResponse
18 | from scrapy.downloadermiddlewares.retry import RetryMiddleware
19 | from twisted.internet.error import TCPTimedOutError
20 |
21 |
22 | class AdvancespiderSpiderMiddleware(object):
23 | # Not all methods need to be defined. If a method is not defined,
24 | # scrapy acts as if the spider middleware does not modify the
25 | # passed objects.
26 |
27 | @classmethod
28 | def from_crawler(cls, crawler):
29 | # This method is used by Scrapy to create your spiders.
30 | s = cls()
31 | crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
32 | return s
33 |
34 | def process_spider_input(response, spider):
35 | # Called for each response that goes through the spider
36 | # middleware and into the spider.
37 |
38 | # Should return None or raise an exception.
39 | return None
40 |
41 | def process_spider_output(response, result, spider):
42 | # Called with the results returned from the Spider, after
43 | # it has processed the response.
44 |
45 | # Must return an iterable of Request, dict or Item objects.
46 | for i in result:
47 | yield i
48 |
49 | def process_spider_exception(response, exception, spider):
50 | # Called when a spider or process_spider_input() method
51 | # (from other spider middleware) raises an exception.
52 |
53 | # Should return either None or an iterable of Response, dict
54 | # or Item objects.
55 | print('========exception=========')
56 |
57 | def process_start_requests(start_requests, spider):
58 | # Called with the start requests of the spider, and works
59 | # similarly to the process_spider_output() method, except
60 | # that it doesn’t have a response associated.
61 |
62 | # Must return only requests (not items).
63 | for r in start_requests:
64 | yield r
65 |
66 | def spider_opened(self, spider):
67 | spider.logger.info('Spider opened: %s' % spider.name)
68 |
69 |
70 | class ProxyMiddleware(object):
71 |
72 | def process_request(self, request, spider):
73 | proxy = random.choice(settings['PROXIES'])
74 | request.meta['proxy'] = proxy
75 |
76 |
77 | class UAMiddleware(object):
78 |
79 | def process_request(self, request, spider):
80 | ua = random.choice(settings['USER_AGENT_LIST'])
81 | request.headers['User-Agent'] = ua
82 |
83 |
84 | class SeleniumMiddleware(object):
85 | def __init__(self):
86 | self.driver = webdriver.Chrome('./chromedriver')
87 |
88 | def process_request(self, request, spider):
89 | if spider.name == 'seleniumSpider':
90 | self.driver.get(request.url)
91 | time.sleep(2)
92 | body = self.driver.page_source
93 |
94 | return HtmlResponse(self.driver.current_url,
95 | body=body,
96 | encoding='utf-8',
97 | request=request)
98 |
99 |
100 | class LoginMiddleware(object):
101 | def __init__(self):
102 | self.client = redis.StrictRedis()
103 |
104 | def process_request(self, request, spider):
105 | if spider.name == 'loginSpider':
106 | cookies = json.loads(self.client.lpop('cookies').decode())
107 | request.cookies = cookies
108 |
109 |
110 | class RetryOfDateMiddleware(RetryMiddleware):
111 | def __init__(self, settings):
112 | RetryMiddleware.__init__(self, settings)
113 |
114 | def process_exception(self, request, exception, spider):
115 | if spider.name == 'exceptionSpider' and isinstance(exception, TCPTimedOutError):
116 | self.remove_borken_proxy(request.meta['proxy'])
117 | return request.copy()
118 |
119 | def remove_borken_proxy(self, proxy):
120 | """
121 | 在这里写代码,从数据库或者是Redis中,把无效的代理剔除掉
122 | :param proxy:
123 | :return:
124 | """
125 | pass
126 |
127 | def process_response(self, request, response, spider):
128 | if spider.name == 'middlewareSpider':
129 | return_str = response.body.decode()
130 | if '404.html' not in str(response.url) and '参数错误' not in return_str:
131 | return response
132 |
133 | yesterday = str(datetime.date.today() - datetime.timedelta(days=1))
134 | if '404.html' in str(response.url):
135 | origin_url = request.meta['redirect_urls'][0]
136 | next_request = request.replace(url=origin_url,
137 | method='POST',
138 | body=json.dumps({'date': yesterday}),
139 | headers={'Content-Type': 'application/json'})
140 | else:
141 | next_request = request.replace(body=json.dumps({'date': yesterday}))
142 | return next_request
143 | return response
144 |
145 |
146 | class ExceptionCheckSpider(object):
147 |
148 | def process_spider_exception(self, response, exception, spider):
149 | if spider.name == 'checkSpider':
150 | print(f'返回的内容是:{response.body.decode()}\n报错原因:{exception}')
151 | page = response.meta['page']
152 | now_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
153 | error_item = ErrorItem()
154 | error_item['page'] = page
155 | error_item['error_time'] = now_time
156 | yield error_item
157 |
--------------------------------------------------------------------------------
/第12章/program/AdvanceSpider/AdvanceSpider/pipelines.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Define your item pipelines here
4 | #
5 | # Don't forget to add your pipeline to the ITEM_PIPELINES setting
6 | # See: http://doc.scrapy.org/en/latest/topics/item-pipeline.html
7 | import pymongo
8 | from scrapy.conf import settings
9 | from AdvanceSpider.items import ErrorItem
10 |
11 |
12 | class AdvancespiderPipeline(object):
13 | def __init__(self):
14 | self.db = pymongo.MongoClient()[settings['MONGODB_DB']]
15 | self.handler = None
16 |
17 | def process_item(self, item, spider):
18 | if isinstance(item, ErrorItem):
19 | self.process_error(item)
20 | return item
21 |
22 | def process_error(self, item):
23 | if not self.handler:
24 | self.handler = self.db[settings['MONGODB_ERROR']]
25 | self.handler.insert_one(dict(item))
26 |
--------------------------------------------------------------------------------
/第12章/program/AdvanceSpider/AdvanceSpider/settings.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Scrapy settings for AdvanceSpider project
4 | #
5 | # For simplicity, this file contains only settings considered important or
6 | # commonly used. You can find more settings consulting the documentation:
7 | #
8 | # http://doc.scrapy.org/en/latest/topics/settings.html
9 | # http://scrapy.readthedocs.org/en/latest/topics/downloader-middleware.html
10 | # http://scrapy.readthedocs.org/en/latest/topics/spider-middleware.html
11 |
12 | BOT_NAME = 'AdvanceSpider'
13 |
14 | SPIDER_MODULES = ['AdvanceSpider.spiders']
15 | NEWSPIDER_MODULE = 'AdvanceSpider.spiders'
16 |
17 |
18 | # Crawl responsibly by identifying yourself (and your website) on the user-agent
19 | #USER_AGENT = 'AdvanceSpider (+http://www.yourdomain.com)'
20 |
21 | # Obey robots.txt rules
22 | ROBOTSTXT_OBEY = False
23 |
24 | # Configure maximum concurrent requests performed by Scrapy (default: 16)
25 | #CONCURRENT_REQUESTS = 32
26 |
27 | # Configure a delay for requests for the same website (default: 0)
28 | # See http://scrapy.readthedocs.org/en/latest/topics/settings.html#download-delay
29 | # See also autothrottle settings and docs
30 | #DOWNLOAD_DELAY = 3
31 | # The download delay setting will honor only one of:
32 | #CONCURRENT_REQUESTS_PER_DOMAIN = 16
33 | #CONCURRENT_REQUESTS_PER_IP = 16
34 |
35 | # Disable cookies (enabled by default)
36 | #COOKIES_ENABLED = False
37 |
38 | # Disable Telnet Console (enabled by default)
39 | #TELNETCONSOLE_ENABLED = False
40 |
41 | # Override the default request headers:
42 | #DEFAULT_REQUEST_HEADERS = {
43 | # 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
44 | # 'Accept-Language': 'en',
45 | #}
46 |
47 | # Enable or disable spider middlewares
48 | # See http://scrapy.readthedocs.org/en/latest/topics/spider-middleware.html
49 | SPIDER_MIDDLEWARES = {
50 | 'AdvanceSpider.middlewares.ExceptionCheckSpider': 543,
51 | }
52 |
53 | # Enable or disable downloader middlewares
54 | # See http://scrapy.readthedocs.org/en/latest/topics/downloader-middleware.html
55 | DOWNLOADER_MIDDLEWARES = {
56 | # 'AdvanceSpider.middlewares.ProxyMiddleware': 543,
57 | 'AdvanceSpider.middlewares.UAMiddleware': 544,
58 | # 'AdvanceSpider.middlewares.RetryOfDateMiddleware': 545,
59 | # 'AdvanceSpider.middlewares.SeleniumMiddleware': 545,
60 |
61 | }
62 |
63 | # Enable or disable extensions
64 | # See http://scrapy.readthedocs.org/en/latest/topics/extensions.html
65 | #EXTENSIONS = {
66 | # 'scrapy.extensions.telnet.TelnetConsole': None,
67 | #}
68 |
69 | # Configure item pipelines
70 | # See http://scrapy.readthedocs.org/en/latest/topics/item-pipeline.html
71 | ITEM_PIPELINES = {
72 | 'AdvanceSpider.pipelines.AdvancespiderPipeline': 300,
73 | }
74 |
75 | # Enable and configure the AutoThrottle extension (disabled by default)
76 | # See http://doc.scrapy.org/en/latest/topics/autothrottle.html
77 | #AUTOTHROTTLE_ENABLED = True
78 | # The initial download delay
79 | #AUTOTHROTTLE_START_DELAY = 5
80 | # The maximum download delay to be set in case of high latencies
81 | #AUTOTHROTTLE_MAX_DELAY = 60
82 | # The average number of requests Scrapy should be sending in parallel to
83 | # each remote server
84 | #AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0
85 | # Enable showing throttling stats for every response received:
86 | #AUTOTHROTTLE_DEBUG = False
87 |
88 | # Enable and configure HTTP caching (disabled by default)
89 | # See http://scrapy.readthedocs.org/en/latest/topics/downloader-middleware.html#httpcache-middleware-settings
90 | #HTTPCACHE_ENABLED = True
91 | #HTTPCACHE_EXPIRATION_SECS = 0
92 | #HTTPCACHE_DIR = 'httpcache'
93 | #HTTPCACHE_IGNORE_HTTP_CODES = []
94 | #HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'
95 |
96 | PROXIES = ['https://114.217.243.25:8118',
97 | 'https://125.37.175.233:8118',
98 | 'http://1.85.116.218:8118']
99 |
100 | USER_AGENT_LIST = [
101 | "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36",
102 | "Dalvik/1.6.0 (Linux; U; Android 4.2.1; 2013022 MIUI/JHACNBL30.0)",
103 | "Mozilla/5.0 (Linux; U; Android 4.4.2; zh-cn; HUAWEI MT7-TL00 Build/HuaweiMT7-TL00) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
104 | "AndroidDownloadManager",
105 | "Apache-HttpClient/UNAVAILABLE (java 1.4)",
106 | "Dalvik/1.6.0 (Linux; U; Android 4.3; SM-N7508V Build/JLS36C)",
107 | "Android50-AndroidPhone-8000-76-0-Statistics-wifi",
108 | "Dalvik/1.6.0 (Linux; U; Android 4.4.4; MI 3 MIUI/V7.2.1.0.KXCCNDA)",
109 | "Dalvik/1.6.0 (Linux; U; Android 4.4.2; Lenovo A3800-d Build/LenovoA3800-d)",
110 | "Lite 1.0 ( http://litesuits.com )",
111 | "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727)",
112 | "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 Safari/537.36 SE 2.X MetaSr 1.0",
113 | "Mozilla/5.0 (Linux; U; Android 4.1.1; zh-cn; HTC T528t Build/JRO03H) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30; 360browser(securitypay,securityinstalled); 360(android,uppayplugin); 360 Aphone Browser (2.0.4)",
114 | ]
115 |
116 | MONGODB_DB = 'AdvanceSpider'
117 | MONGODB_ERROR = 'error'
118 |
--------------------------------------------------------------------------------
/第12章/program/AdvanceSpider/AdvanceSpider/spiders/IpChecker.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import scrapy
3 |
4 |
5 | class IpcheckerSpider(scrapy.Spider):
6 | name = "IpChecker"
7 | # allowed_domains = ["xx.com"]
8 | start_urls = ['http://exercise.kingname.info/exercise_middleware_ua',
9 | 'http://exercise.kingname.info/exercise_middleware_ua/2',
10 | 'http://exercise.kingname.info/exercise_middleware_ua/3']
11 |
12 | def parse(self, response):
13 | print(response.body.decode())
14 |
--------------------------------------------------------------------------------
/第12章/program/AdvanceSpider/AdvanceSpider/spiders/__init__.py:
--------------------------------------------------------------------------------
1 | # This package will contain the spiders of your Scrapy project
2 | #
3 | # Please refer to the documentation for information on how to create and manage
4 | # your spiders.
5 |
--------------------------------------------------------------------------------
/第12章/program/AdvanceSpider/AdvanceSpider/spiders/checkSpider.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import scrapy
3 | import json
4 | import datetime
5 |
6 |
7 | class checkSpider(scrapy.Spider):
8 | name = "checkSpider"
9 | # allowed_domains = ["xx.com"]
10 | start_urls = ['http://exercise.kingname.info/exercise_middleware_retry_backend/param/{}']
11 |
12 | def start_requests(self):
13 | for i in range(1, 10):
14 | url = self.start_urls[0].format(i)
15 | yield scrapy.Request(url,
16 | method='POST',
17 | body=json.dumps({"date": str(datetime.date.today())}),
18 | headers={'Content-Type': 'application/json'},
19 | meta={'page': i})
20 |
21 | def parse(self, response):
22 | if '参数错误' in response.body.decode():
23 | raise Exception('参数错误')
24 |
--------------------------------------------------------------------------------
/第12章/program/AdvanceSpider/AdvanceSpider/spiders/exceptionSpider.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import scrapy
3 |
4 |
5 | class ExceptionSpider(scrapy.Spider):
6 | name = "exceptionSpider"
7 | # allowed_domains = ["xx.com"]
8 | start_urls = ['http://exercise.kingname.info/exercise_middleware_ip',
9 | 'http://exercise.kingname.info/exercise_middleware_ip/2',
10 | 'http://exercise.kingname.info/exercise_middleware_ip/3']
11 |
12 | def parse(self, response):
13 | print(response.body.decode())
14 |
--------------------------------------------------------------------------------
/第12章/program/AdvanceSpider/AdvanceSpider/spiders/loginSpider.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import scrapy
3 |
4 |
5 | class LoginspiderSpider(scrapy.Spider):
6 | name = "loginSpider"
7 | allowed_domains = ["kingname.info"]
8 | start_urls = ['http://exercise.kingname.info/exercise_login_success']
9 |
10 | def parse(self, response):
11 | print(response.body.decode())
12 |
--------------------------------------------------------------------------------
/第12章/program/AdvanceSpider/AdvanceSpider/spiders/middlewareSpider.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import json
3 | import scrapy
4 | import datetime
5 |
6 |
7 | class MiddlewareSpider(scrapy.Spider):
8 | name = "middlewareSpider"
9 | allowed_domains = ["kingname.info"]
10 | start_urls = ['http://exercise.kingname.info/exercise_middleware_retry_backend/404/{}']
11 |
12 | def start_requests(self):
13 | for i in range(1, 10):
14 | url = self.start_urls[0].format(i)
15 | yield scrapy.Request(url,
16 | method='POST',
17 | body=json.dumps({"date": str(datetime.date.today())}),
18 | headers={'Content-Type': 'application/json'})
19 |
20 | def parse(self, response):
21 | print(response.body.decode())
22 |
--------------------------------------------------------------------------------
/第12章/program/AdvanceSpider/AdvanceSpider/spiders/seleniumSpider.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import scrapy
3 |
4 |
5 | class SeleniumspiderSpider(scrapy.Spider):
6 | name = "seleniumSpider"
7 | # allowed_domains = ["xxx.com"]
8 | start_urls = ['http://exercise.kingname.info/exercise_ajax_1.html']
9 |
10 | def parse(self, response):
11 | content_get = response.xpath('//div[@class="content_get"]/text()').extract()[0]
12 | content_post = response.xpath('//div[@class="content_post"]/text()').extract()[0]
13 | print('content_get: {content_get}'.format(content_get=content_get))
14 | print('content_post: {content_post}'.format(content_post=content_post))
15 |
--------------------------------------------------------------------------------
/第12章/program/AdvanceSpider/chromedriver:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingname/SourceCodeOfBook/ab7275108994dca564905818b678bbd2f771c18e/第12章/program/AdvanceSpider/chromedriver
--------------------------------------------------------------------------------
/第12章/program/AdvanceSpider/helper.py:
--------------------------------------------------------------------------------
1 | import time
2 | import json
3 | import redis
4 | from selenium import webdriver
5 |
6 | client = redis.StrictRedis()
7 |
8 | driver = webdriver.Chrome('./chromedriver')
9 | driver.get('http://exercise.kingname.info/exercise_login_success')
10 | user = driver.find_element_by_xpath('//input[@name="username"]')
11 | user.clear()
12 | user.send_keys('kingname')
13 |
14 | password = driver.find_element_by_xpath('//input[@name="password"]')
15 | password.clear()
16 | password.send_keys('genius')
17 |
18 | remember = driver.find_element_by_xpath('//input[@name="rememberme"]')
19 | remember.click()
20 |
21 | login = driver.find_element_by_xpath('//button[@class="login"]')
22 | login.click()
23 | time.sleep(2)
24 | cookies = driver.get_cookies()
25 | client.lpush('cookies', json.dumps(cookies))
26 | driver.quit()
27 |
--------------------------------------------------------------------------------
/第12章/program/AdvanceSpider/main.py:
--------------------------------------------------------------------------------
1 | from scrapy import cmdline
2 | cmdline.execute("scrapy crawl checkSpider".split())
3 |
--------------------------------------------------------------------------------
/第12章/program/AdvanceSpider/scrapy.cfg:
--------------------------------------------------------------------------------
1 | # Automatically created by: scrapy startproject
2 | #
3 | # For more information about the [deploy] section see:
4 | # https://scrapyd.readthedocs.org/en/latest/deploy.html
5 |
6 | [settings]
7 | default = AdvanceSpider.settings
8 |
9 | [deploy]
10 | url = http://45.76.110.210:6800/
11 | project = AdvanceSpider
12 |
--------------------------------------------------------------------------------
/第12章/program/AdvanceSpider/setup.py:
--------------------------------------------------------------------------------
1 | # Automatically created by: scrapyd-deploy
2 |
3 | from setuptools import setup, find_packages
4 |
5 | setup(
6 | name = 'project',
7 | version = '1.0',
8 | packages = find_packages(),
9 | entry_points = {'scrapy': ['settings = AdvanceSpider.settings']},
10 | )
11 |
--------------------------------------------------------------------------------
/第12章/program/DeploySpider/.idea/DeploySpider.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/第12章/program/DeploySpider/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/第12章/program/DeploySpider/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | $USER_HOME$/.subversion
6 | true
7 |
8 |
--------------------------------------------------------------------------------
/第12章/program/DeploySpider/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/第12章/program/DeploySpider/DeploySpider/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingname/SourceCodeOfBook/ab7275108994dca564905818b678bbd2f771c18e/第12章/program/DeploySpider/DeploySpider/__init__.py
--------------------------------------------------------------------------------
/第12章/program/DeploySpider/DeploySpider/items.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Define here the models for your scraped items
4 | #
5 | # See documentation in:
6 | # http://doc.scrapy.org/en/latest/topics/items.html
7 |
8 | import scrapy
9 |
10 |
11 | class DeployspiderItem(scrapy.Item):
12 | # define the fields for your item here like:
13 | # name = scrapy.Field()
14 | pass
15 |
--------------------------------------------------------------------------------
/第12章/program/DeploySpider/DeploySpider/middlewares.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Define here the models for your spider middleware
4 | #
5 | # See documentation in:
6 | # http://doc.scrapy.org/en/latest/topics/spider-middleware.html
7 |
8 | from scrapy import signals
9 |
10 |
11 | class DeployspiderSpiderMiddleware(object):
12 | # Not all methods need to be defined. If a method is not defined,
13 | # scrapy acts as if the spider middleware does not modify the
14 | # passed objects.
15 |
16 | @classmethod
17 | def from_crawler(cls, crawler):
18 | # This method is used by Scrapy to create your spiders.
19 | s = cls()
20 | crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
21 | return s
22 |
23 | def process_spider_input(response, spider):
24 | # Called for each response that goes through the spider
25 | # middleware and into the spider.
26 |
27 | # Should return None or raise an exception.
28 | return None
29 |
30 | def process_spider_output(response, result, spider):
31 | # Called with the results returned from the Spider, after
32 | # it has processed the response.
33 |
34 | # Must return an iterable of Request, dict or Item objects.
35 | for i in result:
36 | yield i
37 |
38 | def process_spider_exception(response, exception, spider):
39 | # Called when a spider or process_spider_input() method
40 | # (from other spider middleware) raises an exception.
41 |
42 | # Should return either None or an iterable of Response, dict
43 | # or Item objects.
44 | pass
45 |
46 | def process_start_requests(start_requests, spider):
47 | # Called with the start requests of the spider, and works
48 | # similarly to the process_spider_output() method, except
49 | # that it doesn’t have a response associated.
50 |
51 | # Must return only requests (not items).
52 | for r in start_requests:
53 | yield r
54 |
55 | def spider_opened(self, spider):
56 | spider.logger.info('Spider opened: %s' % spider.name)
57 |
--------------------------------------------------------------------------------
/第12章/program/DeploySpider/DeploySpider/pipelines.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Define your item pipelines here
4 | #
5 | # Don't forget to add your pipeline to the ITEM_PIPELINES setting
6 | # See: http://doc.scrapy.org/en/latest/topics/item-pipeline.html
7 |
8 |
9 | class DeployspiderPipeline(object):
10 | def process_item(self, item, spider):
11 | return item
12 |
--------------------------------------------------------------------------------
/第12章/program/DeploySpider/DeploySpider/settings.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Scrapy settings for DeploySpider project
4 | #
5 | # For simplicity, this file contains only settings considered important or
6 | # commonly used. You can find more settings consulting the documentation:
7 | #
8 | # http://doc.scrapy.org/en/latest/topics/settings.html
9 | # http://scrapy.readthedocs.org/en/latest/topics/downloader-middleware.html
10 | # http://scrapy.readthedocs.org/en/latest/topics/spider-middleware.html
11 |
12 | BOT_NAME = 'DeploySpider'
13 |
14 | SPIDER_MODULES = ['DeploySpider.spiders']
15 | NEWSPIDER_MODULE = 'DeploySpider.spiders'
16 |
17 |
18 | # Crawl responsibly by identifying yourself (and your website) on the user-agent
19 | #USER_AGENT = 'DeploySpider (+http://www.yourdomain.com)'
20 |
21 | # Obey robots.txt rules
22 | ROBOTSTXT_OBEY = False
23 |
24 | # Configure maximum concurrent requests performed by Scrapy (default: 16)
25 | #CONCURRENT_REQUESTS = 32
26 |
27 | # Configure a delay for requests for the same website (default: 0)
28 | # See http://scrapy.readthedocs.org/en/latest/topics/settings.html#download-delay
29 | # See also autothrottle settings and docs
30 | DOWNLOAD_DELAY = 5
31 | # The download delay setting will honor only one of:
32 | #CONCURRENT_REQUESTS_PER_DOMAIN = 16
33 | #CONCURRENT_REQUESTS_PER_IP = 16
34 |
35 | # Disable cookies (enabled by default)
36 | #COOKIES_ENABLED = False
37 |
38 | # Disable Telnet Console (enabled by default)
39 | #TELNETCONSOLE_ENABLED = False
40 |
41 | # Override the default request headers:
42 | #DEFAULT_REQUEST_HEADERS = {
43 | # 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
44 | # 'Accept-Language': 'en',
45 | #}
46 |
47 | # Enable or disable spider middlewares
48 | # See http://scrapy.readthedocs.org/en/latest/topics/spider-middleware.html
49 | #SPIDER_MIDDLEWARES = {
50 | # 'DeploySpider.middlewares.DeployspiderSpiderMiddleware': 543,
51 | #}
52 |
53 | # Enable or disable downloader middlewares
54 | # See http://scrapy.readthedocs.org/en/latest/topics/downloader-middleware.html
55 | #DOWNLOADER_MIDDLEWARES = {
56 | # 'DeploySpider.middlewares.MyCustomDownloaderMiddleware': 543,
57 | #}
58 |
59 | # Enable or disable extensions
60 | # See http://scrapy.readthedocs.org/en/latest/topics/extensions.html
61 | #EXTENSIONS = {
62 | # 'scrapy.extensions.telnet.TelnetConsole': None,
63 | #}
64 |
65 | # Configure item pipelines
66 | # See http://scrapy.readthedocs.org/en/latest/topics/item-pipeline.html
67 | #ITEM_PIPELINES = {
68 | # 'DeploySpider.pipelines.DeployspiderPipeline': 300,
69 | #}
70 |
71 | # Enable and configure the AutoThrottle extension (disabled by default)
72 | # See http://doc.scrapy.org/en/latest/topics/autothrottle.html
73 | #AUTOTHROTTLE_ENABLED = True
74 | # The initial download delay
75 | #AUTOTHROTTLE_START_DELAY = 5
76 | # The maximum download delay to be set in case of high latencies
77 | #AUTOTHROTTLE_MAX_DELAY = 60
78 | # The average number of requests Scrapy should be sending in parallel to
79 | # each remote server
80 | #AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0
81 | # Enable showing throttling stats for every response received:
82 | #AUTOTHROTTLE_DEBUG = False
83 |
84 | # Enable and configure HTTP caching (disabled by default)
85 | # See http://scrapy.readthedocs.org/en/latest/topics/downloader-middleware.html#httpcache-middleware-settings
86 | #HTTPCACHE_ENABLED = True
87 | #HTTPCACHE_EXPIRATION_SECS = 0
88 | #HTTPCACHE_DIR = 'httpcache'
89 | #HTTPCACHE_IGNORE_HTTP_CODES = []
90 | #HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'
91 |
--------------------------------------------------------------------------------
/第12章/program/DeploySpider/DeploySpider/spiders/Example.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import scrapy
3 |
4 |
5 | class ExampleSpider(scrapy.Spider):
6 | name = "Example"
7 | allowed_domains = ["kingname.info"]
8 | start_urls = ['http://kingname.info/']
9 |
10 | def start_requests(self):
11 | for i in range(20):
12 | yield scrapy.Request('http://exercise.kingname.info/exercise_middleware_ip/{}'.format(i))
13 |
14 | def parse(self, response):
15 | print(response.body.decode())
16 |
17 |
--------------------------------------------------------------------------------
/第12章/program/DeploySpider/DeploySpider/spiders/__init__.py:
--------------------------------------------------------------------------------
1 | # This package will contain the spiders of your Scrapy project
2 | #
3 | # Please refer to the documentation for information on how to create and manage
4 | # your spiders.
5 |
--------------------------------------------------------------------------------
/第12章/program/DeploySpider/scrapy.cfg:
--------------------------------------------------------------------------------
1 | # Automatically created by: scrapy startproject
2 | #
3 | # For more information about the [deploy] section see:
4 | # https://scrapyd.readthedocs.org/en/latest/deploy.html
5 |
6 | [settings]
7 | default = DeploySpider.settings
8 |
9 | [deploy]
10 | url = http://45.76.110.210:6800/
11 | project = DeploySpider
12 | username = kingname
13 | password = genius
14 |
--------------------------------------------------------------------------------
/第12章/program/DeploySpider/scrapy_template.cfg:
--------------------------------------------------------------------------------
1 | # Automatically created by: scrapy startproject
2 | #
3 | # For more information about the [deploy] section see:
4 | # https://scrapyd.readthedocs.org/en/latest/deploy.html
5 |
6 | [settings]
7 | default = DeploySpider.settings
8 |
9 | [deploy]
10 | url = http://$server$/
11 | project = DeploySpider
12 |
--------------------------------------------------------------------------------
/第12章/program/DeploySpider/setup.py:
--------------------------------------------------------------------------------
1 | # Automatically created by: scrapyd-deploy
2 |
3 | from setuptools import setup, find_packages
4 |
5 | setup(
6 | name = 'project',
7 | version = '1.0',
8 | packages = find_packages(),
9 | entry_points = {'scrapy': ['settings = DeploySpider.settings']},
10 | )
11 |
--------------------------------------------------------------------------------
/第12章/program/Requester/.idea/Requester.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/第12章/program/Requester/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/第12章/program/Requester/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | $USER_HOME$/.subversion
6 | true
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/第12章/program/Requester/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/第12章/program/Requester/Bat_Deploy.py:
--------------------------------------------------------------------------------
1 | import requests
2 | import os
3 |
4 | server_list = ['45.76.110.210:6800',
5 | '123.55.11.89.98:6800',
6 | '67.10.123.96:6800',
7 | '77.82.32.10.6:6800'
8 | ]
9 |
10 | start_url = 'http://{server}/schedule.json'
11 | scrapy_project_folder = '/Users/kingname/book/chapter_12/DeploySpider'
12 | scrapy_cfg_template_path = os.path.join(scrapy_project_folder, 'scrapy_template.cfg')
13 | os.chdir(scrapy_project_folder) # 切换工作区,进入爬虫工程根目录执行命令
14 | project = 'DeploySpider'
15 | spider = 'Example'
16 |
17 | with open(scrapy_cfg_template_path, encoding='utf-8') as f:
18 | scrapy_cfg_template = f.read()
19 |
20 |
21 | def deploy(server):
22 | scrapy_cfg = scrapy_cfg_template.replace('$server$', server)
23 | with open('scrapy.cfg', 'w', encoding='utf-8') as f:
24 | f.write(scrapy_cfg)
25 | os.system('scrapyd-deploy')
26 |
27 |
28 | def launch(server):
29 | url = start_url.format(server=server)
30 | start_data = {'project': project,
31 | 'spider': spider}
32 | result = requests.post(url, data=start_data).text
33 | print('服务器 {server}爬虫运行结果:{result}'.format(server=server,
34 | result=result))
35 |
36 |
37 | if __name__ == '__main__':
38 | for server in server_list:
39 | deploy(server)
40 | launch(server)
41 |
--------------------------------------------------------------------------------
/第12章/program/Requester/Launcher.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 |
4 | scrapy_project_path = '/Users/kingname/book/chapter_12/DeploySpider'
5 | os.chdir(scrapy_project_path) #切换工作区,进入爬虫工程根目录执行命令
6 | os.system('scrapyd-deploy')
7 |
8 |
9 | import json
10 | import time
11 | import requests
12 | start_url = 'http://45.76.110.210:6800/schedule.json'
13 | start_data = {'project': 'DeploySpider',
14 | 'spider': 'Example'}
15 |
16 | end_url = 'http://45.76.110.210:6800/cancel.json'
17 | end_data = {'project': 'DeploySpider'}
18 |
19 | result = requests.post(start_url, data=start_data, auth=('kingname', 'genius')).text
20 | result = requests.post(end_url, data=end_data, auth=('kingname', 'genius')).text
21 |
22 | # result_dict = json.loads(result)
23 | # job_id = result_dict['jobid']
24 | # print(f'启动的爬虫,jobid为:{job_id}')
25 | #
26 | # time.sleep(5)
27 | # end_data['job'] = job_id
28 | # result = requests.post(end_url, data=end_data).text
29 | # print(result)
30 |
--------------------------------------------------------------------------------
/第2章/program/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/第2章/program/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/第2章/program/.idea/program.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/第2章/program/Dict_Set.py:
--------------------------------------------------------------------------------
1 |
2 |
3 | # example_dict = {'superman': '超人是一个可以在天上飞的两足兽。它一般喜欢把内裤穿在外面',
4 | # '天才': '天才跑在时代的前面,把时代拖得气喘吁吁。',
5 | # 'xx': 0,
6 | # 42: '42 is the answer of everything.'}
7 | #
8 | # print(example_dict['天才'])
9 | # print(example_dict.get(42))
10 | # print(example_dict.get('不存在的key'))
11 | # print(example_dict.get('不存在的key', '找不到'))
12 | #
13 | #
14 | # existed_dict = {'a': 123, 'b': 456}
15 | # print(existed_dict)
16 | # existed_dict['b'] = '我修改了b'
17 | # print(existed_dict)
18 | # existed_dict['new'] = '我来也'
19 | # print(existed_dict)
20 |
21 | duplicated_list = [3, 1, 3, 2, 4, 6, 6, 7, 's', 's', 'a']
22 | unique_list = list(set(duplicated_list))
23 | print(unique_list)
--------------------------------------------------------------------------------
/第2章/program/ForLoop.py:
--------------------------------------------------------------------------------
1 | # for循环从列表中取出每一个元素
2 | # name_list = ['张三', '李四', '王五', '朱六']
3 | # for name in name_list:
4 | # print(name)
5 |
6 |
7 | # for 循环从字符串里面取出每一个字符
8 | # title = '大家好,我叫kingname.'
9 | # for character in title:
10 | # print(character)
11 |
12 | # for 循环读取字典
13 | # menu_dict = {'红烧肉': '100元', '水煮肉片': '50元', '鸡汤': '1角'}
14 | # for key in menu_dict:
15 | # print('菜品:{}'.format(key))
16 | # print('价格:{}'.format(menu_dict[key]))
17 | # print('=============')
18 |
19 | # for循环仅仅作为循环使用
20 | # for i in range(5):
21 | # print('现在是第{}次循环'.format(i))
22 |
23 | # continue
24 | # name_list = ['张三', '李四', '王五', '朱六']
25 | # for name in name_list:
26 | # if name == '王五':
27 | # continue
28 | # print(name)
29 |
30 | #break
31 | # name_list = ['张三', '李四', '王五', '朱六']
32 | # for name in name_list:
33 | # if name == '王五':
34 | # break
35 | # print(name)
36 |
37 |
--------------------------------------------------------------------------------
/第2章/program/Guess.py:
--------------------------------------------------------------------------------
1 | import random
2 |
3 | history = {}
4 |
5 |
6 | def try_to_guess(name, answer):
7 | try_num = 0
8 | while try_num < 10:
9 | guess_answer = int(input('请输入一个数字: '))
10 | if guess_answer < answer:
11 | print('你输入的数字比正确答案小。')
12 | elif guess_answer == answer:
13 | print('回答正确!')
14 | history[name].append('成功')
15 | break
16 | else:
17 | print('你输入的数字比正确答案大。')
18 | try_num += 1
19 | else:
20 | print('猜错次数太多,失败。')
21 | history[name].append('失败')
22 |
23 |
24 | def show_history():
25 | for name, data in history.items():
26 | print('用户:{}, 记录如下:{}'.format(name, data))
27 |
28 |
29 | def start():
30 | name = input('请输入你的名字:')
31 | if name == '退出':
32 | return
33 | if name not in history:
34 | history[name] = []
35 | answer = random.randint(0, 1024)
36 | try_to_guess(name, answer)
37 |
38 |
39 | def default():
40 | pass
41 |
42 | if __name__ == '__main__':
43 | select_dict = {'1': show_history, '2': start, '3': exit}
44 | while True:
45 | select = input('1.历史记录\n2.继续游戏\n3.退出游戏\n输入数字选择:')
46 | select_dict.get(select, default)()
47 |
--------------------------------------------------------------------------------
/第2章/program/If_Statement.py:
--------------------------------------------------------------------------------
1 | state = 'start'
2 | if state == 'start':
3 | code = 1
4 | elif state == 'running':
5 | code = 2
6 | elif state == 'offline':
7 | code = 3
8 | elif state == 'unknown':
9 | code = 4
10 | else:
11 | code = 5
12 |
13 | state_dict = {'start': 1, 'running': 2, 'offline': 3, 'unknown': 4}
14 | code = state_dict.get(state, 5)
--------------------------------------------------------------------------------
/第2章/program/IntFloatVariable.py:
--------------------------------------------------------------------------------
1 | print(123)
2 | print(3.14159)
3 | print(1 - 10)
4 | print(3 + 2 - 5 * 0)
5 | print((3 + 2 - 5) * 0)
6 |
7 | # 整数的除法和小数的加减乘除
8 | # print(0.1 + 0.2)
9 | # print(1 / 3)
10 | # print(1 * 0.3)
11 | # print(0.1 + 0.2 - 0.3 * 0.4 / 0.5)
12 |
13 | #变量的使用
14 | # 长 = 10
15 | # 宽 = 5
16 | # 高 = 2
17 | #
18 | # 面积 = 长 * 宽
19 | # 体积 = 面积 * 高
20 | # print(面积)
21 | # print(体积)
22 |
--------------------------------------------------------------------------------
/第2章/program/Robot.py:
--------------------------------------------------------------------------------
1 | class Robot(object):
2 | def __init__(self, name):
3 | self.name = name
4 | self.height = 30 #身高30 厘米
5 | self.weight = 5 #体重5千克
6 | self.left_foot_from_earth = 0 #左脚距离地面0厘米
7 | self.right_foot_from_earth = 0 #右键距离地面0厘米
8 | self.left_hand_from_earth = 15 #左手距离地面15厘米
9 | self.right_hand_from_earth = 15 #右手距离地面15厘米
10 |
11 | def _adjust_movement(self, part, current_value, displacement):
12 | """
13 | 脚不能插到地底下,也不能离地高于15厘米。
14 | 手不能低于身体的一半,也不能高于40厘米
15 | :param part: foot 或者 hand
16 | :param displacement: int
17 | :return: int
18 | """
19 | if part == 'foot':
20 | boundary = [0, 15]
21 | elif part == 'hand':
22 | boundary = [15, 40]
23 | else:
24 | print('未知的身体部位!')
25 | return
26 | new_value = current_value + displacement
27 | if new_value < boundary[0]:
28 | return boundary[0]
29 | elif new_value > boundary[1]:
30 | return boundary[1]
31 | else:
32 | return new_value
33 |
34 | def move_left_foot(self, displacement):
35 | left_foot_from_earth = self.left_foot_from_earth + displacement
36 | if left_foot_from_earth > 0 and self.right_foot_from_earth > 0:
37 | print('不能双脚同时离地, 放弃移动左脚!')
38 | return
39 | self.left_foot_from_earth = self._adjust_movement('foot', self.left_foot_from_earth, displacement)
40 | self.announce()
41 |
42 | def move_right_foot(self, displacement):
43 | right_foot_from_earth = self.right_foot_from_earth + displacement
44 | if right_foot_from_earth > 0 and self.left_foot_from_earth > 0:
45 | print('不能双脚同时离地, 放弃移动右脚!')
46 | else:
47 | self.right_foot_from_earth = self._adjust_movement('foot', self.right_foot_from_earth, displacement)
48 | self.announce()
49 |
50 | def move_left_hand(self, displacement):
51 | self.left_hand_from_earth = self._adjust_movement('hand', self.left_hand_from_earth, displacement)
52 | self.announce()
53 |
54 | def move_right_hand(self, displacement):
55 | self.right_hand_from_earth = self._adjust_movement('hand', self.right_hand_from_earth, displacement)
56 | self.announce()
57 |
58 | def announce(self):
59 | print('\n**************************')
60 | print('左手距离地面:{}厘米'.format(self.left_hand_from_earth))
61 | print('右手距离地面:{}厘米'.format(self.right_hand_from_earth))
62 | print('左脚距离地面:{}厘米'.format(self.left_foot_from_earth))
63 | print('右脚距离地面:{}厘米'.format(self.right_foot_from_earth))
64 | print('**************************\n')
65 |
66 | def dance(self):
67 | self.move_left_foot(14)
68 | self.move_right_foot(4)
69 | self.move_right_hand(20)
70 | self.move_right_hand(100)
71 | self.move_right_hand(-5)
72 | self.move_left_foot(-2)
73 |
74 | if __name__ == '__main__':
75 | robot = Robot('瓦力')
76 | robot.dance()
77 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/第2章/program/String_List_Tuple.py:
--------------------------------------------------------------------------------
1 | example_string = '我是字符串'
2 | example_list = ['我', '是', '列', '表']
3 | example_tuple = ('我', '是', '元', '组')
4 | print('1.取第一个元素 >', example_string[0], example_list[0], example_tuple[0])
5 | print('2.取下标为2的元素(第三个元素)>', example_string[2], example_list[2], example_tuple[2])
6 | print('3.取最后一个元素 >', example_string[-1], example_list[-1], example_tuple[-1])
7 | print('4.取倒数第二个元素 >', example_string[-2], example_list[-2], example_tuple[-2])
8 | print('5.切片0:1 >', example_string[0:1], example_list[0:1], example_tuple[0:1])
9 | print('6.切片0:2 >', example_string[0:2], example_list[0:2], example_tuple[0:2])
10 | print('7.切片2:4 >', example_string[2:4], example_list[2:4], example_tuple[2:4])
11 | print('8.切片从第一个元素直到下标为1的元素 >', example_string[:2], example_list[:2], example_tuple[:2])
12 | print('9.切片从下标为1的元素直到全部 >', example_string[1:], example_list[1:], example_tuple[1:])
13 | print('10.切片去掉最后一个元素 >', example_string[:-1], example_list[:-1], example_tuple[:-1])
14 | print('11.切片去掉最后两个元素 >', example_string[:-2], example_list[:-2], example_tuple[:-2])
15 | print('12.每2个字取一个 >', example_string[::2], example_list[::2], example_tuple[::2])
16 | print('13.将字符串、列表、元组倒序输出 >', example_string[::-1], example_list[::-1], example_tuple[::-1])
17 |
18 |
19 | # string_1 = '你好'
20 | # string_2 = '世界'
21 | # string_3 = string_1 + string_2
22 | # print(string_3)
23 | #
24 | #
25 | # list_1 = ['abc', 'xyz']
26 | # list_2 = ['哈哈哈哈', '嘿嘿嘿黑']
27 | # list_3 = list_1 + list_2
28 | # print(list_3)
29 | #
30 | # existed_list = [1, 2, 3]
31 | # existed_list[1] = '新的值'
32 | # print(existed_list)
33 | #
34 | # list_4 = ['Python', '爬虫']
35 | # print(list_4)
36 | # list_4.append('一')
37 | # print(list_4)
38 | # list_4.append('酷')
39 | # print(list_4)
--------------------------------------------------------------------------------
/第2章/program/WhileLoop.py:
--------------------------------------------------------------------------------
1 | i = 0
2 | while i < 10:
3 | print('现在是第{}次循环'.format(i))
4 | i += 1 #千万不要忘记这一行,否则循环就停不下来了
5 |
--------------------------------------------------------------------------------
/第2章/program/class_example.py:
--------------------------------------------------------------------------------
1 | class People(object):
2 | def __init__(self, name, age):
3 | self.name = name
4 | self.age = age
5 | self.jump()
6 |
7 | def walk(self):
8 | print('我的名字叫做:{},我正在走路'.format(self.name))
9 |
10 | def eat(self):
11 | print('我的名字叫做:{},我正在吃饭'.format(self.name))
12 |
13 | def jump(self):
14 | print('我的名字叫做:{},我跳了一下'.format(self.name))
15 |
16 | xiaoer = People('王小二', 18)
17 | zhangsan = People('张三', 30)
18 |
19 | print('=============获取对象的属性=============')
20 | print(xiaoer.name)
21 | print(zhangsan.age)
22 |
23 | print('=============执行对象的方法==============')
24 | xiaoer.walk()
25 | zhangsan.eat()
26 |
--------------------------------------------------------------------------------
/第2章/program/default_fun.py:
--------------------------------------------------------------------------------
1 | def get_input(split_char):
2 | input_string = input('请输入由{}分割的两个非零整数:'.format(split_char))
3 | a_string, b_string = input_string.split(split_char)
4 | return int(a_string), int(b_string)
5 |
6 | # a, b = get_input('#')
7 | # print('第一个数是:{}, 第二个数是:{}'.format(a, b))
8 |
9 | # a, b = get_input() #不小心漏掉了参数就会报错
10 |
11 |
12 | # def get_input_with_default_para(split_char=','):
13 | # input_string = input('请输入由{}分割的两个非零整数:'.format(split_char))
14 | # a_string, b_string = input_string.split(split_char)
15 | # return int(a_string), int(b_string)
16 | #
17 | # c, d = get_input_with_default_para()
18 | # print('第一个数是:{}, 第二个数是:{}'.format(c, d))
19 | #
20 | # e, f = get_input_with_default_para('*')
21 | # print('使用*号分割的第一个数是:{}, 第二个数是:{}'.format(e, f))
22 |
23 |
24 | # def print_x_y_z(x=100, y=0, z=50):
25 | # print('x的值为{}, y的值为{}, z的值为{}'.format(x, y, z))
26 | #
27 | # print_x_y_z(1, 2, 3) #直接写上3个参数
28 | # print_x_y_z(6) #只写一个参数的时候,函数会从左到右依次赋值,
29 | # print_x_y_z(y=-8) #也可以指定参数的名字,将值直接赋给指定的参数
30 | # print_x_y_z(y='哈哈', x='嘿嘿') #如果指定了参数名,那么参数顺序就可以颠倒
31 |
32 | # def default_para_trap(para=[], value=0):
33 | # para.append(value)
34 | # return para
35 | #
36 | # print('第一步')
37 | # print('函数返回值:{}'.format(default_para_trap(value=100)))
38 | # print('第二步')
39 | # print('函数返回值:{}'.format(default_para_trap(value=50)))
40 |
41 |
42 | def default_para_without_trap(para=[], value=0):
43 | if not para:
44 | para = []
45 | para.append(value)
46 | return para
47 |
48 | print('第一步')
49 | print('函数返回值:{}'.format(default_para_without_trap(value=100)))
50 | print('第二步')
51 | print('函数返回值:{}'.format(default_para_without_trap(value=50)))
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/第2章/program/function.py:
--------------------------------------------------------------------------------
1 | # def func_example_1():
2 | # a = 1 + 1
3 | # return a
4 | # b = 2 + 2
5 | # print(b)
6 | #
7 | #
8 | # def func_example_2(x):
9 | # if x <= 0:
10 | # return x
11 | # elif 0 < x <= 1:
12 | # return x * 10
13 | # else:
14 | # return 100
15 | #
16 | # print(func_example_1())
17 |
18 |
19 | a = [1, 2, 3]
20 | b = 0
21 |
22 |
23 | def change_list(para):
24 | para.append(4)
25 | para.append(5)
26 | para.append(6)
27 |
28 |
29 | def change_int(para):
30 | para = 100
31 |
32 | print('列表原来为:{}'.format(a))
33 | change_list(a)
34 | print('列表被修改为:{}'.format(a))
35 |
36 | print('变量原来为:{}'.format(b))
37 | change_int(b)
38 | print('变量现在为:{}'.format(b))
39 |
--------------------------------------------------------------------------------
/第2章/program/input_output.py:
--------------------------------------------------------------------------------
1 | def get_input():
2 | input_string = input('请输入由逗号分割的两个非零整数:')
3 | a_string, b_string = input_string.split(',')
4 | return int(a_string), int(b_string)
5 |
6 |
7 | def calc(a, b):
8 | sum_a_b = a + b
9 | difference_a_b = a - b
10 | product_a_b = a * b
11 | quotient = a / b
12 | return {'sum': sum_a_b, 'diff': difference_a_b, 'pro': product_a_b, 'quo': quotient}
13 |
14 |
15 | def output(result):
16 | print('两个数的和为: {}'.format(result['sum']))
17 | print('两个数的差为: {}'.format(result['diff']))
18 | print('两个数的积为: {}'.format(result['pro']))
19 | print('两个数的商为: {}'.format(result['quo']))
20 |
21 |
22 | def run():
23 | a, b = get_input()
24 | result = calc(a, b)
25 | output(result)
26 |
27 | run()
28 |
29 | # a_int, b_int = get_input()
30 | # result_dict = calc(a_int, b_int)
31 | # output(result_dict)
32 |
--------------------------------------------------------------------------------
/第3章/program/.idea/chapter_3.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/第3章/program/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/第3章/program/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/第3章/program/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/第3章/program/CsvOperator.py:
--------------------------------------------------------------------------------
1 | import csv
2 |
3 | # with open('result.csv', encoding='utf-8') as f:
4 | # reader = [x for x in csv.DictReader(f)]
5 | #
6 | # for row in reader:
7 | # username = row['username']
8 | # content = row['content']
9 | # reply_time = row['reply_time']
10 | # print('用户名:{}, 回复内容:{}'.format(username, content))
11 |
12 | data = [{'name': 'kingname', 'age': 24, 'salary': 99999},
13 | {'name': 'meiji', 'age': 20, 'salary': 100},
14 | {'name': '小明', 'age': 30, 'salary': 'N/A'}]
15 | with open('new.csv', 'w', encoding='utf-8') as f:
16 | writer = csv.DictWriter(f, fieldnames=['name', 'age', 'salary'])
17 | writer.writeheader()
18 | writer.writerows(data)
19 | writer.writerow({'name': '超人', 'age': 999, 'salary': 0})
20 |
--------------------------------------------------------------------------------
/第3章/program/FileOperator.py:
--------------------------------------------------------------------------------
1 | with open('text.txt', encoding='utf-8') as f:
2 | content_list = f.read()
3 | print(content_list)
4 |
5 | with open('new.txt', 'w', encoding='utf-8') as f:
6 | f.write('你好')
7 | f.write('\n===============\n')
8 | f.writelines(['嘿嘿', '跟我学爬虫'])
9 | f.write('\n===============\n')
10 | f.writelines(['爬虫开发\n', '看这本书就够了\n'])
11 |
12 |
--------------------------------------------------------------------------------
/第3章/program/Tieba.py:
--------------------------------------------------------------------------------
1 | import re
2 | import csv
3 |
4 | with open('source.txt', 'r', encoding='UTF-8') as f:
5 | source = f.read()
6 |
7 |
8 | result_list = []
9 |
10 | # 结果是正确的,但是逻辑上有点问题
11 | # username_list = re.findall('username="(.*?)"', source, re.S)
12 | # content_list = re.findall('j_d_post_content ">(.*?)<', source, re.S)
13 | # reply_time_list = re.findall('class="tail-info">(2017.*?)<', source, re.S)
14 | #
15 | # for i in range(len(username_list)):
16 | # result = {'username': username_list[i],
17 | # 'content': content_list[i],
18 | # 'reply_time': reply_time_list[i]}
19 | # result_list.append(result)
20 |
21 | # 逻辑上更合理的代码
22 | # 首先获得包含每一层楼所有信息的大文本块
23 | every_reply = re.findall('l_post l_post_bright j_l_post clearfix "(.*?)p_props_tail props_appraise_wrap', source, re.S)
24 |
25 | # 从每一个大文本块里面提取出各个楼层的发帖人姓名,发帖内容和发帖时间
26 | for each in every_reply:
27 | result = {}
28 | result['username'] = re.findall('username="(.*?)"', each, re.S)[0]
29 | result['content'] = re.findall('j_d_post_content ">(.*?)<', each, re.S)[0].replace(' ', '')
30 | result['reply_time'] = re.findall('class="tail-info">(2017.*?)<', each, re.S)[0]
31 | result_list.append(result)
32 |
33 | with open('tieba.csv', 'w', encoding='UTF-8') as f:
34 | writer = csv.DictWriter(f, fieldnames=['username', 'content', 'reply_time'])
35 | writer.writeheader()
36 | writer.writerows(result_list)
37 |
--------------------------------------------------------------------------------
/第3章/program/new.csv:
--------------------------------------------------------------------------------
1 | name,age,salary
2 | kingname,24,99999
3 | meiji,20,100
4 | 小明,30,N/A
5 | 超人,999,0
6 |
--------------------------------------------------------------------------------
/第3章/program/new.txt:
--------------------------------------------------------------------------------
1 | 你好
2 | ===============
3 | 嘿嘿跟我学爬虫
4 | ===============
5 |
6 | 爬虫开发
7 | 看这本书就够了
8 |
--------------------------------------------------------------------------------
/第3章/program/regex.py:
--------------------------------------------------------------------------------
1 | import re
2 |
3 | # content = '我的微博密码是:1234567,QQ密码是:33445566, 银行卡密码是:888888,Github密码是:999abc999,帮我记住他们'
4 | #
5 | #
6 | # password_list = re.findall(':(.*?),', content)
7 | # name_list = re.findall('名字是(.*?),', content)
8 | # print('找到内容,返回:{}'.format(password_list))
9 | # print('找不到任何内容,返回:{}'.format(name_list))
10 |
11 | # account_content = '微博账号是:kingname, 密码是:12345678, QQ账号是:99999, 密码是:890abcd, 银行卡账号是:000001, 密码是:654321, Github账号是:99999@qq.com, 密码是:7777love8888, 请记住他们。'
12 | #
13 | # account_password = re.findall('账号是:(.*?), 密码是:(.*?),', account_content)
14 | # print('包含多个括号的情况下,返回:{}'.format(account_password))
15 |
16 | # big_string_mutil = '''
17 | # 我是kingname,我的微博密码是:123
18 | # 45678,
19 | # '''
20 | # password_findall_no_flag = re.findall('密码是:(.*?),', big_string_mutil)
21 | # password_findall_flag = re.findall('密码是:(.*?),', big_string_mutil, re.S)
22 | # print('不使用re.S的时候:{}'.format(password_findall_no_flag))
23 | # print('使用re.的时候:{}'.format(password_findall_flag))
24 |
25 |
26 | # content = '我的微博密码是:1234567,QQ密码是:33445566, 银行卡密码是:888888,Github密码是:999abc999,帮我记住他们'
27 | #
28 | # password_search = re.search('密码是:(.*?),', content)
29 | # password_search_not_find = re.search('xxx:(.*?),', content)
30 | # print(password_search)
31 | # print(password_search.group())
32 | # print(password_search.group(0))
33 | # print(password_search.group(1))
34 | # print(password_search_not_find)
35 |
36 | # account_content = '微博账号是:kingname, 密码是:12345678, QQ账号是:99999, 密码是:890abcd, 银行卡账号是:000001, 密码是:654321, Github账号是:99999@qq.com, 密码是:7777love8888, 请记住他们。'
37 | # account_password = re.search('账号是:(.*?), 密码是:(.*?),', account_content)
38 | # print('读取第一个括号的内容: {}'.format(account_password.group(1)))
39 | # print('读取第二个括号的内容: {}'.format(account_password.group(2)))
40 |
41 |
42 | # content = '我的微博密码是:1234567,QQ密码是:33445566, 银行卡密码是:888888,Github密码是:999abc999,帮我记住他们'
43 | # without_question_mark = re.findall('密码是:(.*),', content)
44 | # with_question_mark = re.findall('密码是:(.*?),', content)
45 | # print('不使用问号的结果: {},长度为:{}'.format(without_question_mark, len(without_question_mark)))
46 | # print('使用问号的结果: {},长度为:{}'.format(with_question_mark, len(with_question_mark)))
47 |
48 |
49 | big_small_text = '''
50 | 有效用户:
51 | 姓名: 张三
52 | 姓名: 李四
53 | 姓名: 王五
54 | 无效用户:
55 | 姓名: 不知名的小虾米
56 | 姓名: 隐身的张大侠
57 | '''
58 | # user = re.findall('姓名: (.*?)\n', big_small_text)
59 | # print(user)
60 |
61 | user_big = re.findall('有效用户(.*?)无效用户', big_small_text, re.S)
62 | print('user_big 的值为: {}'.format(user_big))
63 |
64 | user_useful = re.findall('姓名: (.*?)\n', user_big[0])
65 | print('真正有效的人名:{}'.format(user_useful))
66 |
67 | # html = '''
客户端
68 | # 2017-01-01 13:45:00
69 | # '''
70 | #
71 | # result_1 = re.findall('tail-info">(.*?)<', html)
72 | # result_2 = re.findall('tail-info">2017(.*?)<', html)
73 | # result_3 = re.findall('tail-info">(2017.*?)<', html)
74 | # print('括号里只有.*?时,得到的结果:{}'.format(result_1))
75 | # print('2017在括号外面时,得到的结果:{}'.format(result_2))
76 | # print('2017在括号里面时,得到的结果:{}'.format(result_3))
77 |
--------------------------------------------------------------------------------
/第3章/program/result.csv:
--------------------------------------------------------------------------------
1 | username,content,reply_time
2 | 幽暗的灯笼,如图,2016-05-20 01:24
3 | q734660421,无言以对,2016-05-20 01:27
4 | ultramanzero3,整个意大利才对,2016-05-20 09:21
5 | drl1116,罗马、威尼斯难道不是?,2016-05-20 20:08
6 | 暮夜酱,除了主角其它都不会游泳,2016-05-21 11:47
7 | Caps_Lockxzx,阿尼玛斯模拟而已= =,2016-05-21 12:40
8 | 无双飞将潘凤,无言以对。。,2016-05-21 16:47
9 | 帝国元帅马塞纳,嗯,而且不光弗洛伦撒,威尼斯也是,2016-05-24 12:23
10 | 帝国元帅马塞纳,身为船夫不会游泳,2016-05-24 12:24
11 | baby丶旧城失梦,想起了二太爷,2016-05-25 01:10
12 | 呵呵1287,,2016-05-25 16:56
13 | 枫之唏嘘,肯威有个远房表弟叫寇马克,2016-05-25 18:11
14 | 某晨ll,23333,2016-05-29 21:11
15 | 某晨ll,2333333,2016-06-04 21:52
16 | 我是皢暘,hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh想起玩2的时候全都用游泳来躲追杀,2016-06-05 09:55
17 | 米小玉玉小米,我爬上船夫的船,无论离他多远,船夫都会被我挤下船,然后就不见了,2016-06-07 00:12
18 | 小小小大J,盗贼,士兵,船夫,平民,都不会游泳,2016-06-07 09:50
19 | 半斤芝麻豆,刺客1的二太爷遇水即溶。育碧看不下去,e叔就会游泳了,2016-06-12 09:22
20 | 单挑大龙77,整个威尼斯也是只有这个人会游泳,2016-06-14 02:50
21 | 狂怒玉石,刚开始玩黑旗在哈瓦那的那个城堡不知道掷弹兵怎么杀,想起了AC2的老办法,结果TMD都进化了,淹不死,2016-06-15 01:41
22 | 小赛于1,每次把他们推下水都没浮上来?尸体去哪了??沉底儿了?,2016-07-08 18:03
23 | 我的天啊CAO,阿泰尔穷酸,酸溶于水,而EZIO有钱,富得流油,油比水轻,且不溶于水。所以阿泰尔遇水即溶,而EZIO会游泳。,2016-07-09 09:03
24 | lin221818,上次我做任务时吧npc推下水了 然后任务失败,2016-07-17 09:46
25 |
--------------------------------------------------------------------------------
/第3章/program/text.txt:
--------------------------------------------------------------------------------
1 | Python
2 | 爬虫从入门到实战
3 | 网页内容提取仅仅是爬虫的一小部分
4 | 真正有技术含量和考验能力的是突破各种反爬虫策略
5 | 以及应用一些一反常态的技术获得数据
--------------------------------------------------------------------------------
/第3章/program/tieba.csv:
--------------------------------------------------------------------------------
1 | username,content,reply_time
2 | bu堪灰收, ,2017-03-18 12:58
3 | bu堪灰收, ,2017-03-18 12:59
4 | bu堪灰收, ,2017-03-18 13:08
5 | bu堪灰收, ,2017-03-18 13:24
6 | brucelili之歌, 谁在开船?,2017-03-18 13:56
7 | bu堪灰收, ,2017-03-18 17:45
8 | MD还有谁yz, 水的一手好帖,2017-03-18 18:43
9 | dlksjbl, ,2017-03-18 18:59
10 | 信仰丿ASSASSIN, 真·海盗信条,2017-03-18 19:39
11 | 我可能不能在, 请教一下各位朋友:一直想玩,今天在谷歌上下载了但明明手机上没有但却显示已安装这什么情况啊?,2017-03-18 21:58
12 | 我可能不能在, 没法安装,2017-03-18 21:58
13 | 喇嘛阿克, 刺客信条.海盗奇航。,2017-03-18 21:59
14 | 我可能不能在, 请教一下各位怎么下,2017-03-18 22:00
15 | 南区危大侠, ,2017-03-18 22:02
16 | 喇嘛阿克, 一开始是这俩人来到了一个海盗帮派集结地,当好碰上了一起事件,两个领导人在针锋相对,激进派海盗山缪.贝勒米(历史上的黑山缪)推翻了保守投降派的班杰明.荷尼葛德的地位,率众反抗英国海军,阿朗佐还帮荷尼葛德逃走了,2017-03-18 22:03
17 | 喇嘛阿克, 回来以后就和山缪结盟,组成了逗比三人组,开始了组团作死之旅。最后在差不多1717年的时候山缪把自己作死了,2017-03-18 22:05
18 | 喇嘛阿克, 在此期间阿朗佐一共见过了三个黑旗人物,荷尼葛德,黑胡子萨奇,还有疯酒鬼查尔斯.范恩,2017-03-18 22:07
19 | 热情的Dr_Frank, 你……居然会装我们银行的APP,2017-03-18 23:18
20 | 孟婆丶开碗汤彡, 还行,2017-03-19 00:23
21 | 尤文的心, 早,2017-03-19 07:48
22 | 浑浑噩噩d小子, 666,2017-03-19 08:07
23 | sevenez, 国内代理版,除了有无限捞瓶子的bug,其他一切完美,包括翻译、充值。,2017-03-19 08:37
24 | abcx_xx_x, ,2017-03-19 08:57
25 | 弑星者1, 挂着刺客的牌子买着海战的策略,挂羊头卖狗肉,良心游戏越来越少了……,2017-03-19 09:59
26 | 游城stay, 哈哈哈,2017-03-19 11:18
27 | jinisidazhan, 可惜这是能玩的最好的刺客信条手游,看本色那玩意,装个装备都要联网,2017-03-19 12:02
28 | tabulardust96, 以前这游戏刚出的时候我还玩了好久,宝藏全收集加通关,船也是满级的,2017-03-19 13:38
29 | qq603237809, 这是真的啊,2017-03-19 19:52
30 | 恶魔式微笑嗜血, 这是真的呀,2017-03-19 20:26
31 | 遗失of夏至, 这个不是山寨的吧,2017-03-20 00:07
32 |
--------------------------------------------------------------------------------
/第4章/program/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/第4章/program/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/第4章/program/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/第4章/program/.idea/program.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/第4章/program/Chapter_4_animal.py:
--------------------------------------------------------------------------------
1 | import requests
2 | import re
3 | import os
4 | from multiprocessing.dummy import Pool
5 |
6 |
7 | start_url = 'http://www.kanunu8.com/book3/6879/'
8 |
9 |
10 | def get_source(url):
11 | """
12 | 获取网页源代码。
13 | :param url: 网址
14 | :return: 网页源代码
15 | """
16 | html = requests.get(url)
17 | return html.content.decode('gbk') #这个网页需要使用gbk方式解码才能让中文正常显示
18 |
19 |
20 | def get_toc(html):
21 | """
22 | 获取每一章链接,储存到一个列表中并返回。
23 | :param html: 目录页源代码
24 | :return: 每章链接
25 | """
26 | toc_url_list = []
27 | toc_block = re.findall('正文(.*?)', html, re.S)[0]
28 | toc_url = re.findall('href="(.*?)"', toc_block, re.S)
29 | for url in toc_url:
30 | toc_url_list.append(start_url + url)
31 | return toc_url_list
32 |
33 |
34 | def get_article(html):
35 | """
36 | 获取每一章的正文并返回章节名和正文。
37 | :param html: 正文源代码
38 | :return: 章节名,正文
39 | """
40 | chapter_name = re.search('size="4">(.*?)<', html, re.S).group(1)
41 | text_block = re.search('(.*?)
', html, re.S).group(1)
42 | text_block = text_block.replace('
', '')
43 | return chapter_name, text_block
44 |
45 |
46 | def save(chapter, article):
47 | """
48 | 将每一章保存到本地。
49 | :param chapter: 章节名, 第X章
50 | :param article: 正文内容
51 | :return: None
52 | """
53 | os.makedirs('动物农场', exist_ok=True) #如果没有"动物农场文件夹,就创建一个,如果有,则什么都不做"
54 | with open(os.path.join('动物农场', chapter + '.txt'), 'w', encoding='utf-8') as f:
55 | f.write(article)
56 |
57 |
58 | def query_article(url):
59 | """
60 | 根据正文网址获取正文源代码,并调用get_article函数获得正文内容最后保存到本地。
61 | :param url: 正文网址
62 | :return: None
63 | """
64 | article_html = get_source(url)
65 | chapter_name, article_text = get_article(article_html)
66 | save(chapter_name, article_text)
67 |
68 |
69 | if __name__ == '__main__':
70 | toc_html = get_source(start_url)
71 | toc_list = get_toc(toc_html)
72 | pool = Pool(4)
73 | pool.map(query_article, toc_list)
--------------------------------------------------------------------------------
/第4章/program/Chapter_4_compare.py:
--------------------------------------------------------------------------------
1 | import requests
2 | import time
3 | from multiprocessing.dummy import Pool
4 |
5 |
6 | def query(url):
7 | requests.get(url)
8 |
9 | start = time.time()
10 | for i in range(100):
11 | query('https://baidu.com')
12 | end = time.time()
13 | print(f'单线程循环访问100次百度,耗时:{end - start}')
14 |
15 | start = time.time()
16 | url_list = []
17 | for i in range(100):
18 | url_list.append('https://baidu.com')
19 | pool = Pool(5)
20 | pool.map(query, url_list)
21 | end = time.time()
22 | print(f'5线程访问100次百度,耗时:{end - start}')
23 |
24 |
--------------------------------------------------------------------------------
/第4章/program/Chapter_4_dummy.py:
--------------------------------------------------------------------------------
1 | from multiprocessing.dummy import Pool
2 |
3 |
4 | def calc_power2(num):
5 | return num * num
6 |
7 | pool = Pool(3)
8 | origin_num = [x for x in range(10)]
9 | result = pool.map(calc_power2, origin_num)
10 | print(f'计算1-10的平方分别为:{result}')
11 |
12 |
13 |
--------------------------------------------------------------------------------
/第4章/program/Chapter_4_example.py:
--------------------------------------------------------------------------------
1 | import requests
2 | import re
3 |
4 | html = requests.get('http://exercise.kingname.info/exercise_requests_get.html').content.decode()
5 | title = re.search('title>(.*?)<', html, re.S).group(1)
6 | content_list = re.findall('(.*?)<', html, re.S)
7 | content_str = '\n'.join(content_list)
8 | print(f'页面标题为:{title}')
9 | print(f'页面正文内容为:\n{content_str}')
10 |
11 |
12 |
13 | # data = {'name': 'kingname', 'password': '1234567'}
14 | # # html_formdata = requests.post('http://exercise.kingname.info/exercise_requests_post', data=data)
15 | # html_formdata = requests.post('http://exercise.kingname.info/exercise_requests_post', json=data)
16 | # print(html_formdata.content.decode())
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/第4章/program/动物农场/ 第一章.txt:
--------------------------------------------------------------------------------
1 |
2 |
3 | 故事发生在曼纳庄园里。这天晚上,庄园的主人琼斯先生说是已经锁好了鸡棚,但由于他喝得醉意十足,竟把里面的那些小门都忘了关上。他提着马灯踉踉跄跄地穿过院子,马灯光也跟着一直不停地晃来晃去,到了后门,他把靴子一脚一只踢了出去,又从洗碗间的酒桶里舀起最后一杯啤酒,一饮而尽,然后才上床休息。此时,床上的琼斯夫人已是鼾声如雷了。
4 |
5 | 等那边庄主院卧室里的灯光一熄灭,整个庄园窝棚里就泛起一阵扑扑腾腾的骚动。还在白天的时候,庄园里就风传着一件事,说是老麦哲,就是得过“中等白鬃毛”奖的那头雄猪,在前一天晚上作了一个奇怪的梦,想要传达给其他动物。老麦哲(他一直被这样称呼,尽管他在参加展览时用的名字是“威灵顿美神”)在庄园了一直德高望重,所以动物们为了聆听他想要讲的事情,都十分乐意牺牲一小时的睡眠。当时,大家都已经同意,等琼斯先生完全走开后,他们就到大谷仓内集合。
6 |
7 | 在大谷仓一头一个凸起的台子上,麦哲已经安稳地坐在草垫子上了,在他头顶上方的房梁上悬挂着一盏马灯。他已经十二岁了,近来长得有些发胖,但他依然仪表堂堂。尽管事实上他的犬牙从来没有割剪过,这也并不妨碍他面带着智慧和慈祥。不一会,动物们开始陆续赶来,并按各自不同的方式坐稳了。最先到来的是三条狗,布鲁拜尔、杰西和平彻,猪随后走进来,并立即坐在台子前面的稻草上。鸡栖在窗台上,鸽子扑腾上了房梁,羊和牛躺在猪身后并开始倒嚼起来。两匹套四轮货车的马,鲍克瑟和克拉弗,一块赶来,他们走进时走得很慢,每当他们在落下那巨大的毛乎乎的蹄子时,总是小心翼翼,生怕草堆里藏着什么小动物。克拉弗是一匹粗壮而慈爱的母马,接近中年。她在生了第四个小驹之后,体形再也没有能恢复原样。鲍克瑟身材高大,有近两米高的个头,强壮得赛过两匹普通马相加,不过,他脸上长了一道直到鼻子的白毛,多少显得有些戆相。实际上,他确实不怎么聪明,但他坚韧不拔的个性和干活时那股十足的劲头,使他赢得了普遍的尊敬。跟着马后面到的是白山羊穆丽尔,还有那头驴,本杰明。本杰明是庄园里年龄最老的动物,脾气也最糟,他沉默寡言,不开口则已,一开口就少不了说一些风凉话。譬如,他会说上帝给了他尾巴是为了驱赶苍蝇,但他却宁愿没有尾巴也没有苍蝇。庄园里的动物中,唯有他从来没有笑过,要问为什么,他会说他没有看见什么值得好笑的。然而他对鲍克瑟却是真诚相待,只不过没有公开承认罢了。通常,他俩总是一起在果园那边的小牧场上消磨星期天,肩并着肩,默默地吃草。
8 |
9 | 这两匹马刚躺下,一群失去了妈妈的小鸭子排成一溜进了大谷仓,吱吱喳喳,东张西望,想找一处不会被踩上的地方。克拉弗用她粗壮的前腿象墙一样地围住他们,小鸭子偎依在里面,很快就入睡了。莫丽来得很晚,这个愚蠢的家伙,长着一身白生生的毛,是一匹套琼斯先生座车的母马。她扭扭捏捏地走进来,一颠一颠地,嘴里还嚼着一块糖。她占了个靠前的位置,就开始抖动起她的白鬃毛,试图炫耀一番那些扎在鬃毛上的红饰带。猫是最后一个来的,她象往常一样,到处寻找最热乎的地方,最后在鲍克瑟和克拉弗当中挤了进去。在麦哲讲演时,她在那儿自始至终都得意地发出“咕咕噜噜”的声音,压根儿没听进麦哲讲的一个字。
10 |
11 | 那只驯顺了的乌鸦摩西睡在庄主院后门背后的架子上,除他之外,所有的动物都已到场,看到他们都坐稳了,并聚精会神地等待着,麦哲清了清喉咙,开口说道:
12 |
13 | “同志们,我昨晚做了一个奇怪的梦,这个你们都已经听说了,但我想等一会再提它。我想先说点别的事。同志们,我想我和你们在一起呆不了多久了。在我临死之前,我觉得有责任把我已经获得的智慧传授给你们。我活了一辈子,当我独自躺在圈中时,我总在思索,我想我敢说,如同任何一个健在的动物一样,我悟出了一个道理,那就是活在世上是怎么回事。这就是我要给你们讲的问题。
14 |
15 | “那么,同志们,我们又是怎么生活的呢?让我们来看一看吧:我们的一生是短暂的,却是凄惨而艰辛。一生下来,我们得到的食物不过仅仅使我们苟延残喘而已,但是,只要我们还能动一下,我们便会被驱赶着去干活,直到用尽最后一丝力气,一旦我们的油水被榨干,我们就会在难以置信的残忍下被宰杀。在英格兰的动物中,没有一个动物在一岁之后懂得什么是幸福或空闲的涵意。没有一个是自由的。显而易见,动物的一生是痛苦的、备受奴役的一生。
16 |
17 | “但是,这真的是命中注定的吗?那些生长在这里的动物之所以不能过上舒适的生活,难道是因为我们这块土地太贫瘠了吗?不!同志们!一千个不!英格兰土地肥沃,气候适宜,它可以提供丰富的食物,可以养活为数比现在多得多的动物。拿我们这一个庄园来说,就足以养活十二匹马、二十头牛和数百只羊,而且我们甚至无法想象,他们会过得多么舒适,活得多么体面。那么,为什么我们的悲惨境况没有得到改变呢?这是因为,几乎我们的全部劳动所得都被人类窃取走了。同志们,有一个答案可以解答我们的所以问题,我可以把它总结为一个字——人,人就是我们唯一真正的仇敌。把人从我们的生活中消除掉,饥饿与过度劳累的根子就会永远拔掉。
18 |
19 | “人是一种最可怜的家伙,什么都产不了,只会挥霍。那些家伙产不了奶,也下不了蛋,瘦弱得拉不动犁,跑起来也是慢吞吞的,连个兔子都逮不住。可那家伙却是所有动物的主宰,他驱使他们去干活,给他们报偿却只是一点少得不能再少的草料,仅够他们糊口而已。而他们劳动所得的其余的一切则都被他据为己有。是我们流血流汗在耕耘这块土地,是我们的粪便使它肥沃,可我们自己除了这一副空皮囊之外,又得到了什么呢!你们这些坐在我面前的牛,去年一年里,你们已产过多少加仑的奶呢!那些本来可以喂养出许多强壮的牛犊的奶又到哪儿去了呢?每一滴都流进了我们仇敌的喉咙里。还有你们这些鸡、这一年里你们已下了多少只蛋呢?可又有多少孵成了小鸡?那些没有孵化的鸡蛋都被拿到市场上为琼斯和他的伙计们换成了钞票!你呢,克拉弗,你的四匹小马驹到哪儿去了?他们本来是你晚年的安慰和寄托!而他们却都在一岁时给卖掉了,你永远也无法再见到他们了。补偿给你这四次坐月子和在地里劳作的,除了那点可怜的饲料和一间马厩外,还有什么呢?
20 |
21 | “就是过着这样悲惨的生活,我们也不能被允许享尽天年。拿我自己来说,我无可抱怨,因为我算是幸运的。我十二岁了,已有四百多个孩子,这对一个猪来说就是应有的生活了。但是,到头来没有一个动物能逃过那残忍的一刀。你们这些坐在我面前的小肉猪们,不出一年,你们都将在刀架上嚎叫着断送性命。这恐怖就是我们——牛、猪、鸡、羊等等每一位都难逃的结局。就是马和狗的命运也好不了多少。你,鲍克瑟,有朝一日你那强健的肌肉失去了力气,琼斯就会把你卖给屠马商,屠马商会割断你的喉咙,把你煮了给猎狗吃。而狗呢,等他们老了,牙也掉光了,琼斯就会就近找个池塘,弄块砖头拴再他们的脖子上,把他们沉到水底。
22 |
23 | “那么,同志们,我们这种生活的祸根来自暴虐的人类,这一点难道不是一清二楚的吗?只要驱除了人,我们的劳动所得就会全归我们自己,而且几乎在一夜之间,我们就会变得富裕而自由。那么我们应该为此做些什么呢?毫无疑问,奋斗!为了消除人类,全力以赴,不分昼夜地奋斗!同志们,我要告诉你们的就是这个:造反!老实说,我也不知道造反会在何时发生,或许近在一周之内,或许远在百年之后。但我确信,就象看到我蹄子底下的稻草一样确凿无疑,总有一天,正义要申张。同志们,在你们整个短暂的余生中,不要偏离这个目标!尤其是,把我说的福音传给你们的后代,这样,未来的一代一代动物就会继续这一斗争,直到取得最后胜利。
24 |
25 | “记住,同志们,你们的誓愿决不可动摇,你们决不要让任何甜言蜜语把你们引入歧途。当他们告诉你们什么人与动物有着共同利益,什么一方的兴衰就是另一方的兴衰,千万不要听信那种话,那全是彻头彻尾的谎言。人心里想的事情只有他自己的利益,此外别无他有。让我们在斗争中协调一致,情同手足。所以的人都是仇敌,所有的动物都是同志”。
26 |
27 | 就在这时刻,响起了一阵刺耳的嘈杂声。原来,在麦哲讲话时,有四只个头挺大的耗子爬出洞口,蹲坐在后腿上听他演讲,突然间被狗瞧见,幸亏他们迅速窜回洞内,才免遭一死。麦哲抬起前蹄,平静了一下气氛:
28 |
29 | “同志们”,他说,“这里有一点必须澄清。野生的生灵,比如耗子和兔子,是我们的亲友呢还是仇敌?让我们表决一下吧,我向会议提出这个议题:耗子是同志吗?”
30 |
31 | 表决立即进行,压倒多数的动物同意耗子是同志。有四个投了反对票,是三条狗和一只猫。后来才发现他们其实投了两次票,包括反对票和赞成票。麦哲继续说道:
32 |
33 | “我还有一点要补充。我只是重申一下,永远记住你们的责任是与人类及其习惯势不两立。所有靠两条腿行走的都是仇敌,所有靠四肢行走的,或者有翅膀的,都是亲友。还有记住:在同人类作斗争的过程中,我们就不要模仿他们。即使征服了他们,也决不沿用他们的恶习。是动物就决不住在房屋里,决不睡在床上,决不穿衣、喝酒、抽烟,决不接触钞票,从事交易。凡是人的习惯都是邪恶的。而且,千万要注意,任何动物都不能欺压自己的同类。不论是瘦弱的还是强壮的;不论是聪明的还是迟钝的,我们都是兄弟。任何动物都不得伤害其他动物。所有的动物一律平等。
34 |
35 | “现在,同志们,我来谈谈关于昨晚那个梦的事。那是一个在消灭了人类之后的未来世界的梦想,我无法把它描述出来。但它提醒了我一些早已忘却的事情。很多年以前,当我还是头小猪时,我母亲和其他母猪经常唱一只古老的歌,那支歌,连她们也只记得个曲调和头三句歌词。我很小的时候就对那曲调熟悉了。但我也忘了很久了。然而昨天晚上,我又在梦中回想起来了,更妙的是,歌词也在梦中出现,这歌词,我敢肯定,就是很久以前的动物唱的、并且失传很多代的那首歌词。现在我就想唱给你们听听,同志们,我老了,嗓音也沙哑了,但等我把你们教会了,你们会唱得更好的。他叫‘英格兰兽’。”
36 |
37 | 老麦哲清了清嗓子就开始唱了起来,正如他说的那样,他声音沙哑,但唱得很不错。那首歌曲调慷慨激昂,旋律有点介于“Clementine”和“LaCucuracha”之间。歌词是这样的:
38 |
39 | 英格兰兽,爱尔兰兽,
40 |
41 | 普天之下的兽,
42 |
43 | 倾听我喜悦的佳音,
44 |
45 | 倾听那金色的未来。
46 |
47 | 那一天迟早要到来,
48 |
49 | 暴虐的人类终将消灭,
50 |
51 | 富饶的英格兰大地,
52 |
53 | 将只留下我们的足迹。
54 |
55 | 我们的鼻中不再扣环,
56 |
57 | 我们的背上不再配鞍,
58 |
59 | 蹶子、马刺会永远锈蚀
60 |
61 | 不再有残酷的鞭子噼啪抽闪。
62 |
63 | 那难以想象的富裕生活,
64 |
65 | 小麦、大麦、干草、燕麦
66 |
67 | 苜宿、大豆还有甜菜,
68 |
69 | 那一天将全归我侪。
70 |
71 | 那一天我们将自由解放,
72 |
73 | 阳光普照英格兰大地,
74 |
75 | 水会更纯净,
76 |
77 | 风也更柔逸。
78 |
79 | 哪怕我们活不到那一天,
80 |
81 | 但为了那一天我们岂能等闲,
82 |
83 | 牛、马、鹅、鸡
84 |
85 | 为自由务须流血汗。
86 |
87 | 英格兰兽、爱尔兰兽,
88 |
89 | 普天之下的兽,
90 |
91 | 倾听我喜悦的佳音,
92 |
93 | 倾听那金色的未来。
94 |
95 | 唱着这支歌,动物们陷入了情不自禁的亢奋之中。几乎还没有等麦哲唱完,他们已经开始自己唱了。连最迟钝的动物也已经学会了曲调和个别歌词了。聪明一些的,如猪和狗,几分钟内就全部记住了整首歌。然后,他们稍加几次尝试,就突然间齐声合唱起来,整个庄园顿时回荡着这震天动地的歌声。牛哞哞地叫,狗汪汪地吠,羊咩咩地喊,马嘶嘶地鸣,鸭子嘎嘎地唤。唱着这首歌,他们是多么地兴奋,以至于整整连着唱了五遍,要不是中途被打断,他们真有可能唱个通宵。
96 |
97 | 不巧,喧嚣声吵醒了琼斯先生,他自以为是院子中来了狐狸,便跳下床,操起那支总是放在卧室墙角的猎枪,用装在膛里的六号子弹对着黑暗处开了一枪,弹粒射进大谷仓的墙里。会议就此匆匆解散。动物们纷纷溜回自己的窝棚。家禽跳上了他们的架子,家畜卧到了草堆里,顷刻之间,庄园便沉寂下来。
98 |
99 | (感谢中译者张毅、高孝先以及本书电子版的输入者复旦大学的唐薇小姐)
--------------------------------------------------------------------------------
/第4章/program/动物农场/ 第七章.txt:
--------------------------------------------------------------------------------
1 |
2 |
3 | 那是一个寒冷的冬天。狂风暴雨的天气刚刚过去,这又下起了雨夹雪,接着又是大雪纷飞。然后,严寒来了,冰天冻地一般,直到二月才见和缓。动物们都在全力以赴地赶建风车,因为他们都十分清楚:外界正在注视着他们,如果风车不能重新及时建成,那些妒火中烧的人类便会为此幸灾乐祸的。
4 |
5 | 那些人不怀好意,佯称他们不相信风车会是斯诺鲍毁坏的。他们说,风车之所以倒塌纯粹是因为墙座太薄。而动物们认为事实并非如此。不过,他们还是决定这一次要把墙筑到三-厚,而不是上一次的一-半。这就意味着得采集更多的石头。但采石场上好长时间积雪成堆,什么事也干不成。后来,严冬的天气变得干燥了,倒是干了一些活,但那却是一项苦不堪言的劳作,动物们再也不象先前那样满怀希望、信心十足。它们总感到冷,又常常觉得饿。只有鲍克瑟和克拉弗从不气馁。斯奎拉则时不时来一段关于什么劳动的乐趣以及劳工神圣之类的精彩演讲,但使其他动物受到鼓舞更大的,却来自鲍克瑟的踏实肯干和他总是挂在嘴边的口头禅:“我要更加努力工作。”
6 |
7 | 一月份,食物就开始短缺了。谷类饲料急骤减少,有通知说要发给额外的土豆来弥补。可随后却发现由于地窖上面盖得不够厚,绝大部分土豆都已受冻而发软变坏了,只有很少一些还可以吃。这段时间里,动物们已有好些天除了吃谷糠和萝卜外,再也没有别的可吃的了,他们差不多面临着饥荒。
8 |
9 | 对外遮掩这一实情是非常必要的。风车的倒塌已经给人壮了胆,他们因而就捏造出有关动物庄园的新奇的谎言。这一次,外面又谣传说他们这里所有的动物都在饥荒和瘟疫中垂死挣扎,而且说他们内部不断自相残杀,已经到了以同类相食和吞食幼崽度日的地步。拿破仑清醒地意识倒饲料短缺的真相被外界知道后的严重后果,因而决意利用温普尔先生散布一些相反的言论。本来,到目前为止,对温普尔的每周一次来访,动物们还几乎与他没有什么接触。可是这一次,他们却挑选了一些动物,大都是羊,要他们在温普尔能听得到的地方,装作是在无意的聊天中谈有关饲料粮增加的事。这还不够,拿破仑又让储藏棚里那些几乎已是完全空空如也的大箱子满沙子,然后把剩下的饲料粮盖在上面。最后找个适当的借口,把温普尔领到储藏棚,让他瞥上一眼。温普尔被蒙骗过去了,就不断在外界报告说,动物庄园根本不缺饲料云云。
10 |
11 | 然而快到一月底的时候,问题就变得突出了,其关键就是,必须得从某个地方弄到些额外的粮食。而这些天来,拿破仑轻易不露面,整天就呆在庄主院里,那儿的每道门都由气势汹汹的狗把守着。一旦他要出来,也必是一本正经,而且,还有六条狗前呼后拥着,不管谁要走近,那些狗都会吼叫起来。甚至在星期天早晨,他也常常不露面,而由其他一头猪,一般是斯奎拉来发布他的指示。
12 |
13 | 一个星期天早晨,斯奎拉宣布说,所有重新开始下蛋的鸡,必须把鸡蛋上交。因为通过温普尔牵线,拿破仑已经承诺了一项每周支付四百只鸡蛋的合同。这些鸡蛋所赚的钱可买回很多饲粮,庄园也就可以坚持到夏季,那时,情况就好转了。
14 |
15 | 鸡一听到这些,便提出了强烈的抗议。虽然在此之前就已经有过预先通知,说这种牺牲恐怕是必不可少的,但他们并不相信真会发生这种事。此时,他们刚把春季孵小鸡用的蛋准备好,因而便抗议说,现在拿走鸡蛋就是谋财害命。于是,为了搅乱拿破仑的计划,他们在三只年轻的黑米诺卡鸡的带动下,索性豁出去了。他们的做法是飞到椽子上下蛋,鸡蛋落到地上便打得粉碎。这是自琼斯被逐以后第一次带有反叛味的行为。对此,拿破仑立即采取严厉措施。他指示停止给鸡供应饲料,同时下令,任何动物,不论是谁,哪怕给鸡一粒粮食都要被处以死刑。这些命令由狗来负责执行。坚持了五天的鸡最后投降了,又回到了鸡窝里。在这期间共有九只鸡死去,遗体都埋到了果园里,对外则说他们是死于鸡瘟。对于此事,温普尔一点也不知道,鸡蛋按时交付,每周都由一辆食品车来庄园拉一次。
16 |
17 | 这段时间里,一直都没有再见到斯诺鲍。有谣传说他躲在附近的庄园里,不是在福克斯伍德庄园就是在平彻菲尔德庄园。此时,拿破仑和其他庄园的关系也比以前稍微改善了些。碰巧,在庄园的场院里,有一堆十年前在清理一片榉树林时堆在那儿的木材,至今已经很合用了。于是温普尔就建议拿破仑把它卖掉。皮尔金顿先生和弗雷德里克先生都十分想买。可拿破仑还在犹豫,拿不准卖给谁好。大家注意到,每当他似乎要和弗雷德里克先生达成协议的时候,就有谣传说斯诺鲍正躲在福克斯伍德庄园;而当他打算倾向于皮尔金顿时,就又有谣传说斯诺鲍是在平彻菲尔德庄园。
18 |
19 | 初春时节,突然间有一件事震惊了庄园。说是斯诺鲍常在夜间秘密地潜入庄园!动物们吓坏了,躲在窝棚里夜不能寐。据说,每天晚上他都在夜幕的掩护下潜入庄园,无恶不作。他偷走谷子,弄翻牛奶桶,打碎鸡蛋,践踏苗圃,咬掉果树皮。不论什么时候什么事情搞糟了,通常都要推到斯诺鲍身上,要是一扇窗子坏了或者水道堵塞了,准有某个动物断定这是斯诺鲍在夜间干的。储藏棚的钥匙丢了,所有动物都坚信是斯诺鲍给扔到井里去了。奇怪的是,甚至在发现钥匙原来是被误放在一袋面粉底下之后,他们还是这样坚信不移。牛异口同声地声称斯诺鲍在她们睡觉时溜进牛棚,吸了她们的奶。那些在冬天曾给她们带来烦恼的老鼠,也被指责为斯诺鲍的同伙。
20 |
21 | 拿破仑下令对斯诺鲍的活动进行一次全面调查。他在狗的护卫下,开始对庄园的窝棚进行一次仔细的巡回检查,其他动物谦恭地在几步之外尾随着。每走几步,拿破仑就停下来,嗅一嗅地面上是否有斯诺鲍的气味。他说他能借此分辨出斯诺鲍的蹄印。他嗅遍了每一个角落,从大谷仓、牛棚到鸡窝和苹果园,几乎到处都发现了斯诺鲍的踪迹。每到一处他就把嘴伸到地上,深深地吸上几下,便以惊异的语气大叫到:“斯诺鲍!他到过这儿!我能清楚地嗅出来!”一听到“斯诺鲍”,所有的狗都呲牙咧嘴,发出一阵令动物们胆颤心惊的咆哮。
22 |
23 | 动物们被彻底吓坏了。对他们来说,斯诺鲍就象某种看不见的恶魔,浸透在他们周围的空间,以各种危险威胁着他们。到了晚上,斯奎拉把他们召集起来,带着一幅惶恐不安的神情说,他有要事相告。
24 |
25 | “同志们!”斯奎拉边神经质地蹦跳着边大叫道,“发现了一件最为可怕的事,斯诺鲍已经投靠了平彻菲尔德庄园的弗雷德里克了。而那家伙正在策划着袭击我们,企图独占我们的庄园!斯诺鲍将在袭击中给他带路。更糟糕的是,我们曾以为,斯诺鲍的造反是出自于自命不凡和野心勃勃。可我们搞错了,同志们,你们知道真正的动机是什么吗?斯诺鲍从一开始就和琼斯是一伙的!他自始至终都是琼斯的密探。我们刚刚发现了一些他丢下的文件,这一点在那些文件中完全得到了证实。同志们,依我看,这就能说明不少问题了。在牛棚大战中,虽然幸亏他的阴谋没有得逞,但他想使我们遭到毁灭的企图,难道不是我们有目共睹的吗?”
26 |
27 | 大家都怔住了。比起斯诺鲍毁坏风车一事,这一罪孽要严重得多了。但是,他们在完全接受这一点之前,却犹豫了好几分钟,他们都记得,或者自以为还记得,在牛棚大战中,他们曾看到的是斯诺鲍在带头冲锋陷阵,并不时的重整旗鼓,而且,即使在琼斯的子弹已射进它的脊背时也毫不退缩。对此,他们首先就感到困惑不解,这怎么能说明他是站在琼斯一边的呢?就连很少质疑的鲍克瑟也或然不解。他卧在地上,前腿弯在身子底下,眼睛紧闭着,绞尽脑汁想理顺他的思路。
28 |
29 | “我不信,”他说道,“斯诺鲍在牛棚大战中作战勇敢,这是我亲眼看到的。战斗一结束,我们不是就立刻授予他‘一级动物英雄’勋章了吗?”
30 |
31 | “那是我们的失误,同志们,因为我们现在才知道,他实际上是想诱使我们走向灭亡。在我们已经发现的秘密文件中,这一点写得清清楚楚。”
32 |
33 | “但是他负伤了,”鲍克瑟说,“我们都看见他在流着血冲锋。”
34 |
35 | “那也是预谋中的一部分!”斯奎拉叫道,“琼斯的子弹只不过擦了一下他的皮而已。要是你能识字的话,我会把他自己写的文件拿给你看的。他们的阴谋,就是在关键时刻发出一个信号,让斯诺鲍逃跑并把庄园留给敌人。他差不多就要成功了,我甚至敢说,要是没有我们英勇的领袖拿破仑同志,他早就得逞了。难道你们不记得了,就在琼斯一伙冲进院子的时候,斯诺鲍突然转身就逃,于是很多动物都跟着他跑了吗?还有,就在那一会儿,都乱套了,几乎都要完了,拿破仑同志突然冲上前去,大喊:‘消灭人类!’同时咬住了琼斯的腿,这一点难道你们不记得了吗?你们肯定记得这些吧?”斯奎拉一边左右蹦跳,一边大声叫着。
36 |
37 | 既然斯奎拉把那一场景描述得如此形象生动,动物们便似乎觉得,他们果真记得有这么回事。不管怎么说,他们记得在激战的关键时刻,斯诺鲍曾经掉头逃过。但是鲍克瑟还有一些感到不自在。
38 |
39 | 他终于说道:“我不相信斯诺鲍一开始就是一个叛徒。他后来的所作所为是另一回事,但我认为在牛棚大战中,他是一个好同志。”
40 |
41 | “我们的领袖,拿破仑同志,”斯奎拉以缓慢而坚定的语气宣告,“已经明确地——明确了,同志们——声明斯诺鲍一开始就是琼斯的奸细,是的,远在想着起义前就是的。”
42 |
43 | “噢,这就不一样了!如果这是拿破仑同志说的,那就肯定不会错。”鲍克瑟说。
44 |
45 | “这是事实的真相,同志们!”斯奎拉大叫着。但动物们注意到他那闪亮的小眼睛向鲍克瑟怪模怪样地瞥了一眼。在他转身要走时,停下来又强调了一句:“我提醒庄园的每个动物要睁大眼睛。我们有理由相信,眼下,斯诺鲍的密探正在我们中间潜伏着!”
46 |
47 | 四天以后,在下午的晚些时候,拿破仑召集所有的动物在院子里开会。他们集合好后,拿破仑从屋里出来了,佩戴着他的两枚勋章(他最近已授予他自己“一级动物英雄”和“二级动物英雄”勋章),还带着他那九条大狗,那些狗围着他蹦来蹦去,发出让所有动物都毛骨悚然的吼叫。动物们默默地蜷缩在那里,似乎预感到要发生什么可怕的事。
48 |
49 | 拿破仑严厉地站在那儿向下面扫了一眼,接着便发出一声尖细的惊叫。于是,那些狗就立刻冲上前咬住了四头猪的耳朵,把他们往外拖。那四头猪在疼痛和恐惧中嗥叫着,被拖到拿破仑脚下。猪的耳朵流出血来。狗尝到了血腥味,发狂了好一会儿。使所有动物感到惊愕的是,有三条狗向鲍克瑟扑去。鲍克瑟看到他们来了,就伸出巨掌,在半空中逮住一条狗,把他踩在地上。那条狗尖叫着求饶,另外两条狗夹着尾巴飞跑回来了。鲍克瑟看着拿破仑,想知道是该把那狗压死呢还是放掉。拿破仑变了脸色,他厉声喝令鲍克瑟把狗放掉。鲍克瑟抬起掌,狗带着伤哀号着溜走了。
50 |
51 | 喧嚣立即平静下来了。那四头猪浑身发抖地等待发落,面孔上的每道皱纹似乎都刻写着他们的罪状。他们正是抗议拿破仑废除星期天大会议的那四头猪。拿破仑喝令他们坦白罪行。他们没等进一步督促就交代说,他们从斯诺鲍被驱逐以后一直和他保持秘密接触,还配合他捣毁风车,并和他达成一项协议,打算把动物庄园拱手让给弗雷德里克先生。他们还补充说斯诺鲍曾在私下里对他们承认,他过去几年来一直是琼斯的特务,他们刚一坦白完,狗就立刻咬穿了他们的喉咙。这时,拿破仑声色俱厉地质问别的动物还有什么要坦白的。
52 |
53 | 那三这曾经试图通过鸡蛋事件领头闹事的鸡走上前去,说斯诺鲍曾在她们的梦中显现,并煽动她们违抗拿破仑的命令。她们也被杀掉了。接着一只鹅上前坦白,说他曾在去年收割季节藏了六穗谷子,并在当天晚上吃掉了。随后一只羊坦白说她曾向饮水池里撒过尿,她说是斯诺鲍驱使她这么干的。另外两只羊交待道,他们曾经谋杀了一只老公羊,一只十分忠实的拿破仑的信徒,他们在他正患咳嗽时,追着他围着火堆转来转去。这些动物都被当场杀掉了。口供和死刑就这样进行着,直到拿破仑脚前堆起一堆尸体。空气中弥漫着浓重的血腥味,这样的事情自从赶走琼斯以来还一直是闻所未闻的。
54 |
55 | 等这一切都过去了,剩下的动物,除了猪和狗以来,便都挤成一堆溜走了。他们感到震惊,感到害怕,但却说不清到底什么更使他们害怕——是那些和斯诺鲍结成同盟的叛逆更可怕呢,还是刚刚目睹的对这些叛逆的残忍的惩罚更可怕。过去,和这种血流遍地的情景同样可怕的事也时常可见,但对他们来说是一次要阴森得多,因为这就发生在他们自己同志中间。从琼斯逃离庄园至今,没有一个动物杀害过其他动物,就连老鼠也未曾受害。这时,他们已经走到小山包上,干了一半的风车就矗立在那里,大伙不约而同地躺下来,并挤在一起取暖。克拉弗、穆丽尔、本杰明、牛、羊及一群鹅和鸡,实际上,除了那只猫外全都在这儿,猫在拿破仑命令所有动物集合的时候突然失踪了。一时间,大家都默默不语,只有鲍克瑟还继续站着,一边烦躁不安地走来走去,一边用他那又长又黑的尾巴不断地在自己身上抽打着。偶尔还发出一丝惊叫声,最后他说话了。
56 |
57 | “我不明白,我真不愿相信这种事会发生在我们庄园里,这一定得归咎于我们自己的某些失误。要解决这个,我想关键就是要更加努力地工作,从今天起,早上我要提前一个小时起床。”
58 |
59 | 他步履沉重地走开了,走向采石场。到了那儿,他便连续收集了两车石头,并且都拉到风车那里,一直忙到晚上才收工。
60 |
61 | 动物们挤在克拉弗周围默默不语。从他们躺着的地方,可以俯视整个村庄,在那里,动物庄园的绝大部分都尽收眼底。他们看到:狭长的牧场伸向那条大路,耕种过的地里长着茁壮而碧绿的麦苗,还有草滩、树林、饮水池塘,以及庄园里的红色屋顶和那烟囱里冒出的袅袅青烟。这是一个晴朗的春天的傍晚,夕阳的光辉洒在草地和茂盛的丛林上,荡漾着片片金辉。他们此刻忽然想到,这是他们自己的庄园,每一-土地都归他们自己所有,这是他们感到十分惊讶,因为在此之前,他们从未发现这里竟是如此令他们心驰神往。克拉弗看着下面的山坡,热泪不禁涌上眼眶。如果她有办法说出此时的想法的话,她肯定就会这样说,现在的情形可不是几年前他们为推翻人类而努力奋斗的目标,这些可怕的情形以及这种杀戮并不是他们在老麦哲第一次鼓动起义的那天晚上所向往的。对于未来,如果说她还曾有过什么构想,那就一定是构想了这样一个社会:在那里,没有饥饿和鞭子的折磨,一律平等,各尽其能,强者保护弱者,就象是在麦哲讲演的那天晚上,她曾经用前腿保护着那是最后才到的一群小鸭子一样。但现在她不明白,为什么他们现在竟处在一个不敢讲真话的世界里。当那些气势汹汹的狗到处咆哮的时候,当眼看着自己的同志在坦白了可怕的罪行后被撕成碎片而无可奈何的时候,她的心里没有反叛或者违命的念头。她知道,尽管如此,他们现在也比琼斯在的时候强多了,再说,他们的当务之急还是要防备人类卷土重来。不管出了什么事,她都要依然忠心耿耿,辛勤劳动,服从拿破仑的领导,完成交给自己的任务。然而,她仍相信,她和其他的动物曾期望并为之操劳的,并不是今天这般情景;他们建造风车,勇敢地冒着琼斯的枪林弹雨冲锋陷阵也不是为着这些。这就是她所想的,尽管她还一下说不清。
62 |
63 | 最后,她觉得实在找不到什么合适的措词,而只能换个方式来表达,于是便开始唱“英格兰兽”。围在她周围的动物跟着唱起来。他们唱了三遍,唱得十分和谐,但却缓慢而凄然。他们以前还从没有用这种唱法唱过这支歌。
64 |
65 | 他们刚唱完第三遍,斯奎拉就在两条狗的陪同下,面带着要说什么大事的神情向他们走过来。他宣布,遵照拿破仑同志的一项特别命令,“英格兰兽”已被废止了。从今以后禁止再唱这首歌。
66 |
67 | 动物们怔住了。
68 |
69 | “为什么?穆丽尔囔道。
70 |
71 | “不需要了,同志们,”斯奎拉冷冷地说到,‘英格兰兽’是起义用的歌。但起义已经成功,今天下午对叛徒的处决就是最后的行动。另外仇敌已经全部打垮了。我们在‘英格兰兽’中表达的是在当时对未来美好社会的渴望,但这个社会现在已经建立。这首歌明显不再有任何意义了。”
72 |
73 | 他们感到害怕,可是,恐怕还是有些动物要提出抗议。但就在这时,羊大声地咩咩叫起那套老调子来:“四条腿好,两条腿坏。”持续了好几分钟,也就结束了这场争议。
74 |
75 | 于是再也听不到“英格兰兽”这首歌了,取而代之的,是善写诗的梅尼缪斯写的另外一首歌,它是这样开头的:
76 |
77 | 动物庄园,动物庄园,
78 |
79 | 我永远不会损害您!
80 |
81 | 从此,每个星期天早晨升旗之后就唱这首歌,但不知怎么搞的,对动物们来说,无论是词还是曲,这首歌似乎都不再能和“英格兰兽”相提并论了。
82 |
83 | (感谢中译者张毅、高孝先以及本书电子版的输入者复旦大学的唐薇小姐)
--------------------------------------------------------------------------------
/第4章/program/动物农场/ 第三章.txt:
--------------------------------------------------------------------------------
1 |
2 |
3 | 收割牧草时,他们干得多卖力!但他们的汗水并没有白流,因为这次丰收比他们先前期望的还要大。
4 |
5 | 这些活时常很艰难:农具是为人而不是为动物设计的,没有一个动物能摆弄那些需要靠两条后腿站着才能使用的器械,这是一个很大的缺陷。但是,猪确实聪明,他们能想出排除每个困难的办法。至于马呢,他们这些田地了如指掌,实际上,他们比琼斯及其伙计们对刈草和耕地精通得多。猪其实并不干活,只是指导和监督其他动物。他们凭着非凡的学识,很自然地承担了领导工作。鲍克瑟和克拉弗情愿自己套上割草机或者马拉耙机(当然,这时候根本不会用嚼子或者缰绳),迈着沉稳的步伐,坚定地一圈一圈地行进,猪在其身后跟着,根据不同情况,要么吆喝一声“吁、吁,同志!”要么就是“喔、喔,同志!”在搬运和堆积牧草时,每个动物无不尽力服从指挥。就连鸭子和鸡也整天在大太阳下,辛苦地用嘴巴衔上一小撮牧草来来回回忙个不停。最后,他们完成了收获,比琼斯那伙人过去干的活的时间提前了整整两天!更了不起的是,这是一个庄园里前所未有的大丰收。没有半点遗落;鸡和鸭子凭他们敏锐的眼光竟连非常细小的草梗草叶也没有放过。也没有一个动物偷吃哪怕一口牧草。
6 |
7 | 整个夏季,庄园里的工作象时钟一样运行得有条有理,动物也都幸福愉快,而这一切,是他们从前连想都不敢想的。而今,既然所有食物都出自他们自己劳作,自己生产,而不是吝啬的主人施舍的嗟来之食,因而他们吃的是自己所有的食物,每嚼一口都是一种无比的享受。尽管他们还没有什么经验,但随着寄生的人的离去,每一个动物便有了更多的食物,也有了更多的闲暇。他们遇到过不少麻烦,但也都顺利解决了。比如,这年年底,收完玉米后,因为庄园里没有打谷机和脱粒机,他们就有那种古老的方式,踩来踩去地把玉米粒弄下来,再靠嘴巴把秣壳吹掉。面对困难,猪的机灵和鲍克瑟的力大无比总能使他们顺利度过难关。动物们对鲍克瑟赞叹不已。即使在琼斯时期,鲍克瑟就一直是个勤劳而持之以恒的好劳力,而今,他更是一个顶三个,那一双强劲的肩膀,常常象是承担了庄园里所有的活计。从早到晚,他不停地拉呀推呀,总是出现在工作最艰苦的地方。他早就和一只小公鸡约好,每天早晨,小公鸡提前半小时叫醒他,他就在正式上工之前先干一些志愿活,而这些活看起来也是最急需的。无论遇到什么困难和挫折,鲍克瑟的回答总是:“我要更加努力工作”,这句话也是他一直引用的座右铭。
8 |
9 | 但是,每个动物都只能量力而行,比如鸡和鸭子,收获时单靠他们捡拾零落的谷粒,就节约了五蒲式耳的玉米。没有谁偷吃,也没有谁为自己的口粮抱怨,那些过去习以为常的争吵、咬斗和嫉妒也几乎一扫而光。没有或者说几乎没有动物开小差逃工。不过,倒真有这样的事:莫丽不太习惯早晨起来,她还有一个坏毛病,常常借故蹄子里夹了个石子,便丢下地里的活,早早溜走了。猫的表现也多少与众不同。每当有活干的时候,大家就发现怎么也找不到猫了。她会连续几小时不见踪影,直到吃饭时,或者收工后,才若无其事一般重新露面。可是她总有绝妙的理由,咕咕噜噜地说着,简直真诚得叫谁也没法怀疑她动机良好。老本杰明,就是那头驴,起义后似乎变化不大。他还是和在琼斯时期一样,慢条斯理地干活,从不开小差,也从不支援承担额外工作。对于起义和起义的结果,他从不表态。谁要问他是否为琼斯的离去而感到高兴,他就只说一句:“驴都长寿,你们谁都没有见过死驴呢”。面对他那神秘的回答,其他动物只好就此罢休。
10 |
11 | 星期天没有活,早餐比平时晚一个小时,早餐之后,有一项每周都要举行的仪式,从不例外。先是升旗。这面旗是斯诺鲍以前在农具室里找到的一块琼斯夫人的绿色旧台布,上面用白漆画了一个蹄子和犄角,它每星期天早晨在庄主院花园的旗杆上升起。斯诺鲍解释说,旗是绿色的,象征绿色的英格兰大地。而蹄子和犄角象征着未来的动物共和国,这个共和国将在人类最终被铲除时诞生。升旗之后,所有动物列队进入大谷仓,参加一个名为“大会议”的全体会议。在这里将规划出有关下一周的工作,提出和讨论各项决议。别的动物知道怎样表决,但从未能自己提出任何议题。而斯诺鲍和拿破仑则分别是讨论中最活跃的中心。但显而易见,他们两个一直合不来,无论其中一个建议什么,另一个就准会反其道而行之。甚至对已经通过的议题,比如把果园后面的小牧场留给年老体衰的动物,这一个实际上谁都不反对的议题,他们也是同样如此。为各类动物确定退休年龄,也要激烈争论一番。大会议总是随着“英格兰兽”的歌声结束,下午留作娱乐时间。
12 |
13 | 猪已经把农具室当作他们自己的指挥部了。一到晚上,他们就在这里,从那些在庄主院里拿来的书上学习打铁、木工和其他必备的技艺。斯诺鲍自己还忙于组织其他动物加入他所谓的“动物委员会”。他为母鸡设立了“产蛋委员会”,为牛设立了“洁尾社”,还设立了“野生同志再教育委员会”(这个委员会目的在于驯化耗子和兔子),又为羊发起了“让毛更白运动”等等。此外,还组建了一个读写班。为这一切,他真是不知疲倦。但总的来说。这些活动都失败了,例如,驯化野生动物的努力几乎立即流产。这些野生动物仍旧一如既往,要是对他们宽宏大量,他们就公然趁机钻空子。猫参加了“再教育委员会”,很活跃了几天。有动物看见她曾经有一天在窝棚顶上和一些她够不着的麻雀交谈。她告诉麻雀说,动物现在都是同志,任何麻雀,只要他们愿意,都可以到她的爪子上来,并在上面休息,但麻雀们还是对她敬而远之。
14 |
15 | 然而,读书班却相当成功。到了秋季,庄园里几乎所有的动物都不同程度地扫了盲。
16 |
17 | 对猪来说,他们已经能够十分熟练地读写。狗的阅读能力也练得相当不错,可惜他们只对读“七诫”有兴趣。山羊穆丽尔比狗读得还要好,她还常在晚上把从垃圾堆里找来的剪报念给其他动物听。本杰明读得不比任何猪逊色,但从不运用发挥他的本领。他说,据他所知,迄今为止,还没有什么值得读的东西。克拉弗学会了全部字母,可是就拼不成单词。鲍克瑟只能学到字母D,他会用硕大的蹄子在尘土上摹写出A、B、C、D,然后,站在那里,翘着耳朵,目不转睛地盯着,而且还不时抖动一下额毛,竭尽全力地想下一个字母,可总是想不起来。有好几次,真的,他确实学到了E、F、G、H,但等他学会了这几个,又总是发现他已经忘了A、B、C、D。最后,他决定满足于头四个字母,并在每天坚持写上一两遍,以加强记忆。莫丽除了那六个拼出她自己名字的字母Mollie外,再也不肯学点别的。她会用几根细嫩的树枝,非常灵巧地拼出她的名字,然后用一两支鲜花装饰一下,再绕着它们走几圈,赞叹一番。
18 |
19 | 庄园里的其他动物都只学会了一个字母A。另外还有一点,那些比较迟钝的动物,如羊、鸡、鸭子等,还没有学会熟记“七诫”。于是,斯诺鲍经过反复思忖,宣布“七诫”实际上可以简化为一条准则,那就是“四条腿好,两条腿坏”。他说,这条准则包含了动物主义的基本原则,无论是谁,一旦完全掌握了这个准则,便免除了受到人类影响的危险。起初,禽鸟们首先表示反对,因为他们好像也只有两条腿,到斯诺鲍向他们证明这其实不然。
20 |
21 | “同志们”,他说道,“禽鸟的翅膀,是一种推动行进的器官,而不是用来操作和控制的,因此,它和腿是一回事。而人的不同特点是手,那是他们作恶多端的器官。”
22 |
23 | 对这一番长篇大论,禽鸟们并没有弄懂,但他们接受了斯诺鲍的解释。同时,所有这类反应较慢的动物,都开始郑重其事地在心里熟记这个新准则。“四条腿好,两条腿坏”还题写在大谷仓一端的墙上,位于“七诫”的上方,字体比“七诫”还要大。羊一旦在心里记住了这个准则之后,就愈发兴致勃勃。当他们躺在地里时,就经常咩咩地叫着:“四条腿好,两条腿坏!四条腿好,两条腿坏!”一叫就是几个小时,从不觉得厌烦。
24 |
25 | 拿破仑对斯诺鲍的什么委员会没有半点兴趣。他说,比起为那些已经长大成型的动物做的事来说,对年轻一代的教育才更为重要。赶巧,在收割牧草后不久,杰西和布鲁拜尔都崽了,生下了九条强壮的小狗。等这些小狗刚一断奶,拿破仑说他愿意为他们的教育负责,再把它们从母亲身边带走了。他把他们带到一间阁楼上,那间阁楼只有从农具室搭着梯子才能上去。他们处于这样的隔离状态中,庄园里其他动物很快就把他们忘掉了。
26 |
27 | 牛奶的神秘去向不久就弄清了。原来,它每天被掺到猪饲料里。这时,早茬的苹果正在成熟,果园的草坪上遍布着被风吹落的果子。动物们以为把这些果子平均分配乃是理所当然。然而,有一天,发布了这样一个指示,说是让把所有被风吹落下来的苹果收集起来,带到农具室去供猪食用。对此,其他有些动物嘟嘟囔囔地直发牢骚,但是,这也无济于事。所有的猪对此都完全赞同,甚至包括斯诺鲍和拿破仑在内。斯奎拉奉命对其他动物作些必要的解释。
28 |
29 | “同志们”,他大声嚷道,“你们不会把我们猪这样做看成是出于自私和特权吧?我希望你们不。实际上,我们中有许多猪根本不喜欢牛奶和苹果。我自己就很不喜欢。我们食用这些东西的唯一目的是要保护我们的健康。牛奶和苹果(这一点已经被科学所证明,同志们)包含的营养对猪的健康来说是绝对必需的。我们猪是脑力劳动者。庄园的全部管理和组织工作都要依靠我们。我们夜以继日地为大家的幸福费尽心机。因此,这是为了你们,我们才喝牛奶,才吃苹果的。你们知道吧,万一我们猪失职了,那会发生什么事情呢?琼斯会卷土重来!是的,琼斯会卷土重来!真的,同志们!”斯奎拉一边左右蹦跳着,一边甩动着尾巴,几乎恳求地大喊道:“真的,你没有谁想看到琼斯卷土重来吧?”
30 |
31 | 此时,如果说还有那么一件事情动物们能完全肯定的话,那就是他们不愿意让琼斯回来。当斯奎拉的见解说明了这一点以后,他们就不再有什么可说的了。使猪保持良好健康的重要性再也清楚不过了。于是,再没有继续争论,大家便一致同意:牛奶和被风吹落的苹果(并且还有苹果成熟后的主要收获)应当单独分配给猪。
32 |
33 | (感谢中译者张毅、高孝先以及本书电子版的输入者复旦大学的唐薇小姐)
--------------------------------------------------------------------------------
/第4章/program/动物农场/ 第九章.txt:
--------------------------------------------------------------------------------
1 |
2 |
3 | 鲍克瑟蹄掌上的裂口过了很长时间才痊愈。庆祝活动结束后第二天,动物们就开始第三次建造风车了。对此,鲍克瑟哪里肯闲着,他一天不干活都不行,于是就忍住伤痛不让他们有所察觉。到了晚上他悄悄告诉克拉弗,他的掌子疼得厉害。克拉弗就用嘴巴嚼着草药给他敷上。她和本杰明一起恳求鲍克瑟干活轻一点。她对他说:“马肺又不能永保不衰。”但鲍克瑟不听,他说,他剩下的唯一一个心愿就是在他到退休年龄之前,能看到风车建设顺利进行。
4 |
5 | 想当初,当动物庄园初次制定律法时,退休年龄分别规定为:马和猪十二岁,牛十四岁,狗九岁,羊七岁,鸡和鹅五岁,还允诺要发给充足的养老津贴。虽然至今还没有一个动物真正领过养老津贴,但近来这个话题讨论得越来越多了。眼下,因为苹果园那边的那块小牧场已被留作大麦田,就又有小道消息说大牧场的一角要围起来给退休动物留作牧场用。据说,每匹马的养老津贴是每天五磅谷子,到冬天是每天十五磅干草,公共节假日里还发给一根胡萝卜,或者尽量给一个苹果。鲍克瑟的十二岁生日就在来年的夏末。
6 |
7 | 这个时期的生活十分艰苦。冬天象去年一样冷,食物也更少了。除了那些猪和狗以外,所有动物的饲料粮再次减少。斯奎拉解释说,在定量上过于教条的平等是违背动物主义原则的。不论在什么情况下,他都毫不费力地向其他动物证明,无论表面现象是什么,他们事实上并不缺粮。当然,暂时有必要调整一下供应量(斯奎拉总说这是“调整”,从不认为是“减少”)。但与琼斯时代相比,进步是巨大的。为了向大家详细说明这一点,斯奎拉用他那尖细的嗓音一口气念了一大串数字。这些数字反映出,和琼斯时代相比,他们现在有了更多的燕麦、干草、萝卜,工作的时间更短,饮用的水质更好,寿命延长了,年轻一代的存活率提高了,窝棚里有了更多的草垫,而且跳蚤少多了。动物们对他所说的每句话无不信以为真。说实话,在他们的记忆中,琼斯及他所代表的一切几乎已经完全淡忘了。他们知道,近来的生活窘困而艰难,常常是饥寒交迫,醒着的时候就是干活,但毫无疑问,过去更糟糕。他们情愿相信这些。再说,那时他们是奴隶,现在却享有自由。诚如斯奎拉那句总是挂在嘴上的话所说,这一点使一切都有了天壤之别。
8 |
9 | 现在有更多的嘴要吃饭。这天,四头母猪差不多同时都下小崽,共有三十一头。他们生下来就带着黑白条斑。谁是他们的父亲呢?这并不难推测,因为拿破仑是庄园里唯一的种猪。有通告说,过些时候,等买好了砖头和木材,就在庄主院花园里为他们盖一间学堂。目前,暂时由拿破仑在庄主院的厨房里亲自给他们上课。这些小猪平常是在花园里活动,而且不许他们和其他年幼的动物一起玩耍。大约与此同时,又颁布了一项规定,规定说当其他的动物在路上遇到猪时,他们就必须要站到路边;另外,所有的猪,不论地位高低,均享有星期天在尾巴上戴饰带的特权。
10 |
11 | 庄园度过了相当顺利的一年,但是,他们的钱还是不够用。建学堂用的砖头、沙子、石灰和风车用的机器得花钱去买。庄主院需要的灯油和蜡烛,拿破仑食用的糖(他禁止其他猪吃糖,原因是吃糖会使他们发胖),也得花钱去买。再加上所有日用的勤杂品,诸如工具、钉子、绳子、煤、铁丝、铁块和狗食饼干等等,开销不小。为此,又得重新攒钱。剩余的干草和部分土豆收成已经卖掉,鸡蛋合同又增加到每周六百个。因此在这一年中,孵出的小鸡连起码的数目都不够,鸡群几乎没法维持在过去的数目水平上。十二月份已经减少的口粮,二月份又削减了一次,为了省油,窝棚里也禁止点灯。但是,猪好像倒很舒服,而且事实上,即使有上述情况存在,他们的体重仍有增加。二月末的一个下午,有一股动物们以前从没有闻到过的新鲜、浓郁、令他们馋涎欲滴的香味,从厨房那一边小酿造房里飘过院子来,那间小酿造房在琼斯时期就已弃置不用了。有动物说,这是蒸煮大麦的味道。他们贪婪地嗅着香气,心里都在暗自猜测:这是不是在为他们的晚餐准备热乎乎的大麦糊糊。但是,晚饭时并没有见到热乎乎的大麦糊糊。而且在随后的那个星期天,又宣布了一个通告,说是从今往后,所有的大麦要贮存给猪用。而在此之前,苹果园那边的田里就早已种上了大麦。不久,又传出这样一个消息,说是现在每头猪每天都要领用一品脱啤酒,拿破仑则独自领用半磅,通常都是盛在德贝郡出产的瓷制的带盖汤碗里。
12 |
13 | 但是,不管受了什么气,不管日子多么难熬,只要一想到他们现在活得比从前体面,他们也就觉得还可以说得过去。现在歌声多,演讲多,活动多。拿破仑已经指示,每周应当举行一次叫做“自发游行”的活动,目的在于庆祝动物庄园的奋斗成果和兴旺景象。每到既定时刻,动物们便纷纷放下工作,列队绕着庄园的边界游行,猪带头,然后是马、牛、羊,接着是家禽。狗在队伍两侧,拿破仑的黑公鸡走在队伍的最前头。鲍克瑟和克拉弗还总要扯着一面绿旗,旗上标着蹄掌和犄角,以及“拿破仑同志万岁!”的标语。游行之后,是背诵赞颂拿破仑的诗的活动,接着是演讲,由斯奎拉报告饲料增产的最新数据。而且不时还要鸣枪庆贺。羊对“自发游行”活动最为热心,如果哪个动物抱怨(个别动物有时趁猪和狗不在场就会发牢骚)说这是浪费时间,只不过意味着老是站在那里受冻,羊就肯定会起响亮地叫起“四条腿号,两条腿坏”,顿时就叫得他们哑口无言。但大体上说,动物们搞这些庆祝活动还是兴致勃勃的。归根到底,他们发现正是在这些活动中,他们才感到他们真正是当家做主了,所做的一切都是在为自己谋福利,想到这些,他们也就心满意足。因而,在歌声中,在游戏中,在斯奎拉列举的数字中,在鸣枪声中,在黑公鸡的啼叫声中,在绿旗的飘扬中,他们就可以至少在部分时间里忘却他们的肚子还是空荡荡的。
14 |
15 | 四月份,动物庄园宣告成为“动物共和国”,在所难免的是要选举一位总统,可候选人只有一个,就是拿破仑,他被一致推举就任总统。同一天,又公布了有关斯诺鲍和琼斯串通一气的新证据,其中涉及到很多详细情况。这样,现在看来,斯诺鲍不仅诡计多端地破坏“牛棚大战”,这一点动物们以前已有印象了,而且是公开地为琼斯作帮凶。事实上,正是他充当了那伙人的元凶,他在参加混战之前,还高喊过“人类万岁!”有些动物仍记得斯诺鲍背上带了伤,但那实际上是拿破仑亲自咬的。
16 |
17 | 仲夏时节,乌鸦摩西在失踪数年之后,突然又回到庄园。他几乎没有什么变化,照旧不干活,照旧口口声声地讲着“蜜糖山”的老一套。谁要是愿意听,他就拍打着黑翅膀飞到一根树桩上,滔滔不绝地讲起来:“在那里,同志们,”他一本正经地讲着,并用大嘴巴指着天空——“在那里,就在你们看到的那团乌云那边——那儿有座‘蜜糖山’。那个幸福的国度将是我们可怜的动物摆脱了尘世之后的归宿!”他甚至声称曾在一次高空飞行中到过那里,并看到了那里一望无际的苜蓿地,亚麻子饼和方糖就长在树篱上。很多动物相信了他的话。他们推想,他们现在生活在饥饿和劳累之中,那么换一种情形,难道就不该合情合理地有一个好得多的世界吗?难以谈判的是猪对待摩西的态度,他们都轻蔑地称他那些“蜜糖山”的说法全是谎言,可是仍然允许他留在庄园,允许他不干活,每天还给他一吉尔的啤酒作为补贴。
18 |
19 | 鲍克瑟的蹄掌痊愈之后,他干活就更拼命了。其实,在这一年,所有的动物干起活来都象奴隶一般。庄园里除了那些常见的活和第三次建造风车的事之外,还要给年幼的猪盖学堂,这一工程是在三月份动工的。有时,在食不果腹的情况下长时间劳动是难以忍受的,但鲍克瑟从未退缩过。他的一言一行没有任何迹象表明他的干劲不如过去,只是外貌上有点小小的变化:他的皮毛没有以前那么光亮,粗壮的腰部似乎也有点萎缩。别的动物说:“等春草长上来时,鲍克瑟就会慢慢恢复过来”;但是,春天来了,鲍克瑟却并没有长胖。有时,当他在通往矿顶的坡上,用尽全身气力顶着那些巨型圆石头的重荷的时候,撑持他的力量仿佛唯有不懈的意志了。这种时候,他总是一声不吭,但猛地看上去,似乎还隐约见到他口中念念有词“我要更加努力工作”。克拉弗和本杰明又一次警告他,要当心身体,但鲍克瑟不予理会。他的十二岁生日临近了,但他没有放在心上,而一心一意想的只是在领取养老津贴之前把石头攒够。
20 |
21 | 夏天的一个傍晚,快到天黑的时候,有个突如其来的消息传遍整个庄园,说鲍克瑟出了什么事。在这之前,他曾独自外出,往风车那里拉了一车石头。果然,消息是真的。几分钟后两只鸽子急速飞过来,带来消息说:“鲍克瑟倒下去了!他现在正-着身体躺在那里,站不起来了!”
22 |
23 | 庄园里大约有一半动物冲了出去,赶到建风车的小山包上。鲍克瑟就躺在那里。他在车辕中间伸着脖子,连头也抬不起来,眼睛眨巴着,两肋的毛被汗水粘得一团一团的,嘴里流出一股稀稀的鲜血。克拉弗跪倒在他的身边。
24 |
25 | “鲍克瑟!”她呼喊道,“你怎么啦?”
26 |
27 | “我的肺,”鲍克瑟用微弱的声音说,“没关系,我想没有我你们也能建成风车,备用的石头已经积攒够了。我充其量只有一个月时间了。不瞒你说,我一直盼望着退休。眼看本杰明年老了,说不定他们会让他同时退休,和我作个伴。”
28 |
29 | “我们会得到帮助的,”克拉弗叫到,“快,谁跑去告诉斯奎拉出事啦。”
30 |
31 | 其他动物全都立即跑回庄主院,向斯奎拉报告这一消息,只有克拉弗和本杰明留下来。本杰明躺在鲍克瑟旁边,不声不响地用他的长尾巴给鲍克瑟赶苍蝇。大约过了一刻钟,斯奎拉满怀同情和关切赶到现场。他说拿破仑同志已得知此事,对庄园里这样一位最忠诚的成员发生这种不幸感到十分悲伤,而且已在安排把鲍克瑟送往威灵顿的医院治疗。动物们对此感到有些不安,因为除了莫丽和斯诺鲍之外,其他动物从未离开过庄园,他们不愿想到把一位患病的同志交给人类。然而,斯奎拉毫不费力地说服了他们,他说在威灵顿的兽医院比在庄园里能更好地治疗鲍克瑟的病。大约过了半小时,鲍克瑟有些好转了,他好不容易才站起来,一步一颤地回到他的厩棚,里面已经由克拉弗和本杰明给他准备了一个舒适的稻草床。
32 |
33 | 此后两天里,鲍克瑟就呆在他的厩棚里。猪送来了一大瓶红色的药,那是他们在卫生间的药柜里发现的,由克拉弗在饭后给鲍克瑟服用,每天用药两次。晚上,她躺在他的棚子里和他聊天,本杰明给他赶苍蝇。鲍克瑟声言对所发生的事并不后悔。如果他能彻底康复,他还希望自己能再活上三年。他盼望着能在大牧场的一角平平静静地住上一阵。那样的话,他就能第一次腾出空来学习,以增长才智。他说,他打算利用全部余生去学习字母表上还剩下的二十二个字母。
34 |
35 | 然而,本杰明和克拉弗只有在收工之后才能和鲍克瑟在一起。而正是那一天中午,有一辆车来了,拉走了鲍克瑟。当时,动物们正在一头猪的监视下忙着在萝卜地里除草,忽然,他们惊讶地看着本杰明从庄园窝棚那边飞奔而来,一边还扯着嗓子大叫着。这是他们第一次见到本杰明如此激动,事实上,也是第一次看到他奔跑。“快,快!”他大声喊着,“快来呀!他们要拉走鲍克瑟!”没等猪下命令,动物们全都放下活计,迅速跑回去了。果然,院子里停着一辆大篷车,由两匹马拉着,车边上写着字,驾车人的位置上坐着一个男人,阴沉着脸,头戴一顶低檐圆礼帽。鲍克瑟的棚子空着。
36 |
37 | 动物们围住车,异口同声地说:“再见,鲍克瑟!再见!”
38 |
39 | “笨蛋!傻瓜!”本杰明喊着,绕着他们一边跳,一边用他的小蹄掌敲打着地面:“傻瓜!你们没看见车边上写着什么吗?”
40 |
41 | 这下子,动物们犹豫了,场面也静了下来。穆丽尔开始拼读那些字。可本杰明却把她推到了一边,他自己就在死一般的寂静中念到:
42 |
43 | “‘威灵顿,艾夫列-西蒙兹,屠马商兼煮胶商,皮革商兼供应狗食的骨粉商。’你们不明白这是什么意思吗?他们要把鲍克瑟拉到在宰马场去!”
44 |
45 | 听到这些,所有的动物都突然迸发出一阵恐惧的哭嚎。就在这时,坐在车上的那个人扬鞭催马,马车在一溜小跑中离开大院。所有的动物都跟在后面,拼命地叫喊着。克拉弗硬挤到最前面。这时,马车开始加速,克拉弗也试图加快她那粗壮的四肢赶上去,并且越跑越快,“鲍克瑟!”她哭喊道,“鲍克瑟!鲍克瑟!鲍克瑟!”恰在这时,好像鲍克瑟听到了外面的喧嚣声,他的面孔,带着一道直通鼻子的白毛,出现在车后的小窗子里。
46 |
47 | “鲍克瑟!”克拉弗凄厉地哭喊道,“鲍克瑟!出来!快出来!他们要送你去死!”
48 |
49 | 所有的动物一齐跟着哭喊起来,“出来,鲍克瑟,快出来!”但马车已经加速,离他们越来越远了。说不准鲍克瑟到底是不是听清了克拉弗喊的那些话。但不一会,他的脸从窗上消失了,接着车内响起一阵巨大的马蹄踢蹬声。他是在试图踹开车子出来。按说只要几下,鲍克瑟就能把车厢踢个粉碎。可是天啊!时过境迁,他已没有力气起了;一忽儿,马蹄的踢蹬声渐渐变弱直至消失了。奋不顾身的动物便开始恳求拉车的两匹马停下来,“朋友,朋友!”他们大声呼喊,“别把你们的亲兄弟拉去送死!”但是那两匹愚蠢的畜牲,竟然傻得不知道这是怎么回事,只管竖起耳朵加速奔跑。鲍克瑟的面孔再也没有出现在窗子上。有的动物想跑到前面关上五栅门,但是太晚了,一瞬间,马车就已冲出大门,飞快地消失在大路上。再也见不到鲍克瑟了。
50 |
51 | 三天之后,据说他已死在威灵顿的医院里,但是,作为一匹马,他已经得到了无微不至的照顾。这个消息是由斯奎拉当众宣布的,他说,在鲍克瑟生前的最后几小时里,他一直守候在场。
52 |
53 | “那是我见到过的最受感动的场面!”他一边说,一边抬起蹄子抹去一滴泪水,“在最后一刻我守在他床边。临终前,他几乎衰弱得说不出话来,他凑在我的耳边轻声说,他唯一遗憾的是在风车建成之前死去。他低声说:‘同志们,前进!以起义的名义前进,动物庄园万岁!拿破仑同志万岁!拿破仑永远正确。’同志们,这些就是他的临终遗言。”
54 |
55 | 讲到这里,斯奎拉忽然变了脸色,他沉默一会,用他那双小眼睛射出的疑神疑鬼的目光扫视了一下会场,才继续讲下去。
56 |
57 | 他说,据他所知,鲍克瑟给拉走后,庄园上流传着一个愚蠢的、不怀好意的谣言。有的动物注意到,拉走鲍克瑟的马车上有“屠马商”的标记,就信口开河地说,鲍克瑟被送到宰马场了。他说,几乎难以置信竟有这么傻的动物。他摆着尾巴左右蹦跳着,愤愤地责问,从这一点来看,他们真的很了解敬爱的领袖拿破仑同志吗?其实,答案十分简单,那辆车以前曾归一个屠马商所有,但兽医院已买下了它,不过他们还没有来得及把旧名字涂掉。正是因为这一点,才引起大家的误会。
58 |
59 | 动物们听到这里,都大大地松了一口气。接着斯奎拉继续绘声绘色地描述着鲍克瑟的灵床和他所受到的优待,还有拿破仑为他不惜一切代价购置的贵重药品等等细节。于是他们打消了最后一丝疑虑,想到他们的同志在幸福中死去,他们的悲哀也消解了。
60 |
61 | 在接下来那个星期天早晨的会议上,拿破仑亲自到会,为向鲍克瑟致敬宣读了一篇简短的悼辞。他说,已经不可能把他们亡故的同志的遗体拉回来并埋葬在庄园里了。但他已指示,用庄主院花园里的月桂花做一个大花圈,送到鲍克瑟的墓前。并且,几天之后,猪还打算为向鲍克瑟致哀举行一追悼宴会。最后,拿破仑以“我要更加努力工作”和“拿破仑同志永远正确”这两句鲍克瑟心爱的格言结束了他的讲话。在提到这两句格言时,他说,每个动物都应该把这两句格言作为自己的借鉴,并认真地贯彻到实际行动中去。
62 |
63 | 到了确定为宴会的那一天,一辆杂货商的马车从威灵顿驶来,在庄主院交付了一只大木箱。当天晚上,庄主院里传来一阵鼓噪的歌声,在此之后,又响起了另外一种声音,听上去象是在激烈地吵闹,这吵闹声直到十一点左右的时候,在一阵打碎了玻璃的巨响声中才静了下来。直到第二天中午之前,庄主院不见任何动静。同时,又流传着这样一个小道消息,说猪先前不知从哪里搞到了一笔钱,并给他们又买了一箱威士忌。
64 |
65 | (感谢中译者张毅、高孝先以及本书电子版的输入者复旦大学的唐薇小姐)
--------------------------------------------------------------------------------
/第4章/program/动物农场/ 第二章.txt:
--------------------------------------------------------------------------------
1 |
2 |
3 | 三天之后,老麦哲在安睡中平静地死去。遗体埋在苹果园脚下。
4 |
5 | 这是三月初的事。
6 |
7 | 从此以后的三个月里,有很多秘密活动。麦哲的演讲给庄园里那些比较聪明的动物带来了一个全新的生活观念。他们不知道麦哲预言的造反什么时候才能发生,他们也无法想象造反会在他们有生之年内到来。但他们清楚地晓得,为此作准备就是他们的责任。训导和组织其他动物的工作,自然地落在猪的身上,他们被一致认为是动物中最聪明的。而其中最杰出的是两头名叫斯诺鲍和拿破仑的雄猪,他们是琼斯先生为出售喂养的。拿破仑是头伯克夏雄猪,也是庄园中唯一的伯克夏种,个头挺大,看起来很凶,说话不多,素以固执而出名。相比之下,斯诺鲍要伶俐多了,口才好,也更有独创性,但看起来个性上没有拿破仑那么深沉。庄园里其他的猪都是肉猪。他们中最出名的是一头短小而肥胖的猪,名叫斯奎拉。他长着圆圆的面颊,炯炯闪烁的眼睛,动作敏捷,声音尖细,是个不可多得的演说家。尤其是在阐述某些艰深的论点时,他习惯于边讲解边来回不停地蹦跳,同时还甩动着尾巴。而那玩意儿不知怎么搞地就是富有蛊惑力。别的动物提到斯奎拉时,都说他能把黑的说成白的。
8 |
9 | 这三头猪把老麦哲的训导用心琢磨,推敲出一套完整的思想体系,他们称之为“动物主义”。每周总有几个夜晚,等琼斯先生入睡后,他们就在大户仓里召集秘密会议,向其他动物详细阐述动物主义的要旨。起初,他们针对的是那些迟钝和麻木的动物。这些动物中,有一些还大谈什么对琼斯先生的忠诚的义务,把他视为“主人”,提出很多浅薄的看法,比如“琼斯先生喂养我们,如果他走了,我们会饿死的”。等等。还有的问到这样的问题:“我们干嘛要关心我们死后才能发生的事情?”或者问:“如果造反注定要发生,我们干不干又有什么关系?”因而,为了教他们懂得这些说法都是与动物主义相悖离的,猪就下了很大的功夫。这愚蠢的问题是那匹白雌马莫丽提出来的,她向斯诺鲍最先问的问题是:“造反以后还有糖吗?”
10 |
11 | “没有”,斯诺鲍坚定地说,“我们没有办法在庄园制糖,再说,你不需要糖,而你想要的燕麦和草料你都会有的”。
12 |
13 | “那我还能在鬃毛上扎饰带吗?”莫丽问。
14 |
15 | “同志”,斯诺鲍说,“那些你如此钟爱的饰带全是奴隶的标记。你难道不明白自由比饰带更有价值吗?”
16 |
17 | 莫丽同意了,但听起来并不十分肯定。
18 |
19 | 猪面对的更困难的事情,是对付那只驯顺了的乌鸦摩西散布的谎言。摩西这个琼斯先生的特殊宠物,是个尖细和饶舌的家伙,还是个灵巧的说客。他声称他知道有一个叫做“蜜糖山”的神秘国度,那里是所有动物死后的归宿。它就在天空中云层上面的不远处。摩西说,在蜜糖山,每周七天,天天都是星期天,一年四季都有苜蓿,在那里,方糖和亚麻子饼就长在树篱上。动物们憎恶摩西,因为他光说闲话而不干活,但动物中也有相信蜜糖山的。所以,猪不得不竭力争辩,教动物们相信根本就不存在那么一个地方。
20 |
21 | 他们最忠实的追随者是那两匹套货车的马,鲍克瑟和克拉弗。对他们俩来说,靠自己想通任何问题都很困难。而一旦把猪认作他们的导师,他们便吸取了猪教给他们的一切东西,还通过一些简单的讨论把这些道理传授给其他的动物。大谷仓中的秘密会议,他们也从不缺席。每当会议结束要唱那首“英格兰兽”时,也由他们带头唱起。
22 |
23 | 这一阵子,就结果而言,造反之事比任何一个动物所预期的都要来得更早也更顺利。在过去数年间,琼斯先生尽管是个冷酷的主人,但不失为一位能干的庄园主,可是近来,他正处于背运的时候,打官司中赔了钱,他更沮丧沉沦,于是拼命地喝酒。有一阵子,他整日呆在厨房里,懒洋洋地坐在他的温莎椅上,翻看着报纸,喝着酒,偶尔把干面包片在啤酒里沾一下喂给摩西。他的伙计们也无所事事,这不守职。田地里长满了野草,窝棚顶棚也漏了,树篱无人照管,动物们饥肠辘辘。
24 |
25 | 六月,眼看到了收割牧草的时节。在施洗约翰节的前夕,那一天是星期六,琼斯先生去了威灵顿,在雷德兰喝了个烂醉,直到第二天,也就是星期天的正午时分才赶回来。他的伙计们一大早挤完牛奶,就跑出去打兔子了,没有操心给动物添加草料。而琼斯先生一回来,就在客厅里拿了一张《世界新闻》报盖在脸上,在沙发上睡着了。所以一直到晚上,动物们还没有给喂过。他们终于忍受不住了,有一头母牛用角撞开了贮藏棚的门,于是,所有的动物一拥而上,自顾自地从饲料箱里抢东西。就在此刻,琼斯先生醒了。不一会儿,他和他的四个伙计手里拿着鞭子出现在贮藏棚,上来就四处乱打一气。饥饿的动物哪里还受到了这个,尽管毫无任何预谋,但都不约而同地,猛地扑向这些折磨他们的主人。琼斯先生一伙忽然发现他们自己正处在四面被围之中。被犄角抵,被蹄子踢,形势完全失去了控制。他们从前还没有见到动物这样的举动,他们曾经是怎样随心所欲的鞭笞和虐待这一群畜牲!而这群畜牲们的突然暴动吓得他们几乎不知所措。转眼工夫,他们放弃自卫,拔腿便逃。又过了个把分钟,在动物们势如破竹的追赶下,他们五个人沿着通往大路的车道仓皇败逃。
26 |
27 | 琼斯夫人在卧室中看到窗外发生的一切,匆忙拆些细软塞进一个毛毡手提包里,从另一条路上溜出了庄园。摩西从他的架子上跳起来,扑扑腾腾地尾随着琼斯夫人,呱呱地大声叫着。这时,动物们已经把琼斯一伙赶到外面的大路上,然后砰地一声关上五栅门。就这样,在他们几乎还没有反应过来时,造反已经完全成功了:琼斯被驱逐了,曼纳庄园成了他们自己的。
28 |
29 | 起初,有好大一会,动物们简直不敢相信他们的好运气。他们做的第一件事就是沿着庄园奔驰着绕了一圈,仿佛是要彻底证实一下再也没有人藏在庄园里了。接着,又奔回窝棚中,把那些属于可憎的琼斯统治的最后印迹消除掉。马厩端头的农具棚被砸开了,嚼子、鼻环、狗用的项圈,以及琼斯先生过去常为阉猪、阉羊用的残酷的刀子,统统给丢进井里。缰绳、笼头、眼罩和可耻的挂在马脖子上的草料袋,全都与垃圾一起堆到院中,一把火烧了。鞭子更不例外。动物们眼看着鞭子在火焰中烧起,他们全都兴高采烈的欢呼雀跃起来。斯诺鲍还把饰带也扔进火里,那些饰带是过去常在赶集时扎在马鬃和马尾上用的。
30 |
31 | “饰带”,他说道,“应该视同衣服,这是人类的标记。所有的动物都应该一丝不挂”。
32 |
33 | 鲍克瑟听到这里,便把他夏天戴的一顶小草帽也拿出来,这顶草帽本来是防止蝇虫钻入耳朵才戴的,他也把它和别的东西一道扔进了人火中。
34 |
35 | 不大一会儿,动物们便把所有能引起他们联想到琼斯先生的东西全毁完了。然后,拿破仑率领他们回到贮藏棚里,给他们分发了双份玉米,给狗发了双份饼干。接着,他们从头至尾把“英格兰兽”唱了七遍。然后安顿下来,而且美美睡了一夜,好象他们还从来没有睡过觉似的。
36 |
37 | 但他们还是照常在黎明时醒来,转念想起已经发生了那么了不起的事情,他们全都跑出来,一起冲向大牧场。通向牧场的小路上,有一座小山包,在那里,可以一览整个庄园的大部分景色。动物们冲到小山包顶上,在清新的晨曦中四下注视。是的,这是他们的——他们目光所及的每一件东西都是他们的!在这个念头带来的狂喜中,他们兜着圈子跳呀、蹦呀,在喷涌而来的极度激动中,他们猛地蹦到空中。他们在露水上打滚,咀嚼几口甜润的夏草;他们踢开黑黝黝的田土,使劲吮吸那泥块中浓郁的香味。然后,他们巡视庄园一周,在无声的赞叹中查看了耕地、牧场、果树园、池塘和树丛。仿佛他们以前还从没有见到过这些东西似的。而且,就是在这个时刻,他们还是不敢相信这些都是他们自己的。
38 |
39 | 后来,他们列队向庄园的窝棚走去,在庄主院门外静静地站住了。这也是他们的,可是,他们却惶恐得不敢进去。过一会儿,斯诺鲍和拿破仑用肩撞开门,动物们才鱼贯而入,他们小心翼翼地走着,生怕弄乱了什么。他们踮起蹄子尖一个屋接一个屋地走过,连比耳语大一点的声音都不敢吱一下,出于一种敬畏,目不转睛地盯着这难以置信的奢华,盯着镜子、马鬃沙发和那些用他们的羽绒制成的床铺,还有布鲁塞尔毛圈地毯,以及放在客厅壁炉台上的维多利亚女王的平版肖像。当他们拾级而下时,发现莫丽不见了。再折身回去,才见她呆在后面一间最好的卧室里。她在琼斯夫人的梳妆台上拿了一条蓝饰带,傻下唧唧地在镜子前面贴着肩臭美起来。在大家严厉的斥责下,她这才又走了出来。挂在厨房里的一些火腿也给拿出去埋了,洗碗间的啤酒桶被鲍克瑟踢了个洞。除此之外,房屋里任何其他东西都没有动过。在庄主院现场一致通过了一项决议:庄主院应保存起来作为博物馆。大家全都赞成:任何动物都不得在次居住。
40 |
41 | 动物们用完早餐,斯诺鲍和拿破仑再次召集起他们。
42 |
43 | “同志们”,斯诺鲍说道,“现在是六点半,下面还有整整一天。今天我们开始收割牧草,不过,还有另外一件事情得先商量一下”。
44 |
45 | 这时,大家才知道猪在过去的三个月中,从一本旧的拼读书本上自学了阅读和书写。那本书曾是琼斯先生的孩子的,早先被扔到垃圾堆里。拿破仑叫拿来几桶黑漆和白漆,带领大家来到朝着大路的五栅门。接着,斯诺鲍(正是他才最擅长书写)用蹄子的双趾捏起一支刷子,涂掉了栅栏顶的木牌上的“曼纳庄园”几个字,又在那上面写上“动物庄园”。这就是庄园以后的名字。写完后,他们又回到窝棚那里,斯诺鲍和拿破仑又叫拿来一架梯子,并让把梯子支在大谷仓的墙头。他们解释说,经过过去三个月的研讨,他们已经成功地把动物主义的原则简化为“七戒”,这“七戒”将要题写在墙上,它们将成为不可更改的法律,所有动物庄园的动物都必须永远遵循它生活。斯诺鲍好不容易才爬了上去(因为猪不易的梯子上保持平衡)并开始忙乎起来,斯奎拉在比他低几格的地方端着油漆桶。在刷过柏油的墙上,用巨大的字体写着“七诫”。字是白色的,在三十码以外清晰可辨。它们是这样写的:
46 |
47 | 七诫
48 |
49 | 1.凡靠两条腿行走者皆为仇敌;
50 |
51 | 2.凡靠四肢行走者,或者长翅膀者,皆为亲友;
52 |
53 | 3.任何动物不得着衣;
54 |
55 | 4.任何动物不得卧床;
56 |
57 | 5.任何动物不得饮酒;
58 |
59 | 6.任何动物不得伤害其他动物;
60 |
61 | 7.所有动物一律平等。
62 |
63 | 写得十分潇洒,除了把亲友“friend”写成了“freind”,以及其中有一处“S”写反之外,全部拼写得很正确。斯诺鲍大声念给别的动物听,所有在场的动物都频频点头,表示完全赞同。较为聪明一些的动物立即开始背诵起来。
64 |
65 | “现在,同志们”,斯诺鲍扔下油漆刷子说道,“到牧场上去!我们要争口气,要比琼斯他们一伙人更快地收完牧草”。
66 |
67 | 就在这时刻,早已有好大一会显得很不自在的三头母牛发出振耳的哞哞声。已经二十四小时没有给她们挤奶了。她们的xx子快要胀破了。猪稍一寻思,让取来奶桶,相当成功地给母牛挤了奶,他们的蹄子十分适于干这个活。很快,就挤满了五桶冒着沫的乳白色牛奶,许多动物津津有味地瞧着奶桶中的奶。
68 |
69 | “这些牛奶可怎么办呢?”有一个动物问答。
70 |
71 | “琼斯先生过去常常给我们的谷糠饲料中掺一些牛奶”,有只母鸡说道。
72 |
73 | “别理会牛奶了,同志们!”站在奶桶前的拿破仑大声喊道,“牛奶会给照看好的,收割牧草才更重了,斯诺鲍同志领你们去,我随后就来。前进,同志们!牧草在等待着!”
74 |
75 | 于是,动物们成群结队地走向大牧场,开始了收割。当他们晚上收工回来的时候,大家注意的:牛奶已经不见了。
76 |
77 | (感谢中译者张毅、高孝先以及本书电子版的输入者复旦大学的唐薇小姐)
--------------------------------------------------------------------------------
/第4章/program/动物农场/ 第五章.txt:
--------------------------------------------------------------------------------
1 |
2 |
3 | 冬天快要到了,莫丽变得越来越讨厌。她每天早上干活总要迟到,而且总为自己开脱说她睡过头了,她还常常诉说一些不可思议的病痛,不过,她的食欲却很旺盛。她会找出种种借口逃避干活而跑到饮水池边,呆呆地站在那儿,凝视着她在水中的倒影。但还有一些传闻,说起来比这更严重一些。有一天,当莫丽边晃悠着她的长尾巴边嚼着一根草根,乐悠悠的闲逛到院子里时,克拉弗把她拉到一旁。
4 |
5 | “莫丽”,她说,“我有件非常要紧的事要对你说,今天早晨,我看见你在查看那段隔开动物庄园和福克斯伍德庄园的树篱时,有一个皮尔金顿先生的伙计正站在树篱的另一边。尽管我离得很远,但我敢肯定我看见他在对你说话,你还让他摸你的鼻子。这是怎么回事,莫丽?”
6 |
7 | “他没摸!我没让!这不是真的!”莫丽大声嚷着,抬起前蹄子搔着地。
8 |
9 | “莫丽!看着我,你能向我发誓,那人不是在摸你的鼻子。”
10 |
11 | “这不是真的!”莫丽重复道,但却不敢正视克拉弗。然后,她朝着田野飞奔而去,逃之夭夭。
12 |
13 | 克拉弗心中闪过一个念头。谁也没有打招呼,她就跑到莫丽的厩棚里,用蹄子翻开一堆草。草下竟藏着一堆方糖和几条不同颜色的饰带。
14 |
15 | 三天后,莫丽不见了,好几个星期下落不明。后来鸽子报告说他们曾在威灵顿那边见到过她,当时,她正被驾在一辆单驾马车上,那辆车很时髦,漆得有红有黑,停在一个客栈外面。有个红脸膛的胖子,身穿方格子马裤和高筒靴,象是客栈老板,边抚摸着她的鼻子边给她喂糖。她的毛发修剪一新,额毛上还佩戴着一条鲜红的饰带。所以鸽子说,她显得自鸣得意。从此以后,动物们再也不提她了。
16 |
17 | 一月份,天气极其恶劣。田地好象铁板一样,什么活都干不成。倒是在大谷仓里召开了很多会议,猪忙于筹划下一季度的工作。他们明显比其它动物聪明,也就自然而然地该对庄园里所有的大政方针做出决定,尽管他们的决策还得通过大多数表决同意后才有效。本来,要是斯诺鲍和拿破仑相互之间不闹别扭,整个程序会进行得很顺利。可是在每一个论点上,他们俩一有可能便要抬杠。如果其中一个建议用更大面积播种大麦,另一个则肯定要求用更大面积播种燕麦;如果一个说某某地方最适宜种卷心菜,另一个就会声称那里非种薯类不可,不然就是废地一块。他们俩都有自己的追随者,相互之间还有一些激烈的争辩。在大会议上,斯诺鲍能言善辩,令绝大多数动物心诚口服。而拿破仑更擅长在会议上休息时为争取到支持游说拉票。在羊那儿,他尤其成功。后来,不管适时不适时,羊都在咩咩地叫着“四条腿好,两条腿坏”,并经常借此来捣乱大会议。而且,大家注意到了,越是斯诺鲍的讲演讲到关键处,他们就越有可能插进“四条腿好,两条腿坏”的咩咩声。斯诺鲍曾在庄主院里找到一些过期的《农场主和畜牧业者》杂志,并对此作过深入的研究,装了满脑子的革新和发明设想。他谈起什么农田排水、什么饲料保鲜、什么碱性炉渣,学究气十足。他还设计出一个复杂的系统,可以把动物每天在不同地方拉的粪便直接通到地里,以节省运送的劳力。拿破仑自己无所贡献,却拐弯抹角地说斯诺鲍的这些东西最终将会是一场空,看起来他是在走着瞧了。但是在他们所有的争吵中,最为激烈的莫过于关于风车一事的争辩。
18 |
19 | 在狭长的大牧场上,离庄园里的窝棚不远的地方,有一座小山包,那是庄园里的制高点。斯诺鲍在勘察过那地方之后,宣布说那里是建造风车最合适的地方。这风车可用来带动发电机,从而可为庄园提供电力。也就可以使窝棚里用上电灯并在冬天取暖,还可以带动圆锯、铡草机、切片机和电动挤奶机。动物们以前还从未听说过任何这类事情(因为这是一座老式的庄园,只有一台非常原始的机器)。当斯诺鲍绘声绘色地描述着那些奇妙的机器的情景时,说那些机器可以在他们悠闲地在地里吃草时,在他们修养心性而读书或聊天时为他们干活,动物们都听呆了。
20 |
21 | 不出几个星期,斯诺鲍为风车作的设计方案就全部拟订好了。机械方面的详细资料大多取自于《对居室要做的1000件益事》、《自己做自己的瓦工》和《电学入门》三本书,这三本书原来也是琼斯先生的。斯诺鲍把一间小棚作为他的工作室,那间小棚曾是孵卵棚,里面铺着光滑的木制地板,地板上适宜于画图。他在那里闭门不出,一干就是几个小时。他把打开的书用石块压着,蹄子的两趾间夹着一截粉笔,麻利地来回走动,一边发出带点兴奋的哼哧声,一边画着一道接一道的线条。渐渐地,设计图深入到有大量曲柄和齿轮的复杂部分,图面覆盖了大半个地板,这在其他动物看来简直太深奥了,但印象却非常深刻。他们每天至少要来一次,看看斯诺鲍作图。就连鸡和鸭子也来,而且为了不踩踏粉笔线还格外小心谨慎。惟独拿破仑回避着。一开始,他就声言反对风车。然而有一天,出乎意料,他也来检查设计图了。他沉闷不语地在棚子里绕来绕去,仔细查看设计图上的每一处细节,偶尔还冲着它们从鼻子里哼哼一两声,然后乜斜着眼睛,站在一旁往图上打量一阵子,突然,他抬起腿来,对着图撒了一泡尿,接了一声不吭,扬长而去。
22 |
23 | 整个庄园在风车一事上截然地分裂开了。斯诺鲍毫不否认修建它是一项繁重的事业,需要采石并筑成墙,还得制造叶片,另外还需要发电机和电缆(至于这些如何兑现,斯诺鲍当时没说)。但他坚持认为这项工程可在一年内完成。而且还宣称,建成之后将会因此节省大量的劳力,以至于动物们每周只需要干三天活。另一方面,拿破仑却争辩说,当前最急需的是增加食料生产,而如果他们在风车上浪费时间,他们全都会饿死的。在“拥护斯诺鲍和每周三日工作制”和“拥护拿破仑和食料满槽制”的不同口号下,动物们形成了两派,本杰明是唯一一个两边都不沾的动物。他既不相信什么食料会更充足,也不相信什么风车会节省劳力。他说,有没有风车无所谓,生活会继续下去的,一如既往,也就是说总有不足之处。
24 |
25 | 除了风车争执之外,还有一个关于庄园的防御问题。尽管人在牛棚大战中被击溃了,但他们为夺回庄园并使琼斯先生复辟,会发动一次更凶狠的进犯,这是千真万确的事。进一步说,因为他们受到挫败的消息已经传遍了整个国家,使得附近庄园的动物比以前更难驾驭了,他们也就更有理由这样干了。可是斯诺鲍和拿破仑又照例发生了分歧。根据拿破仑的意见,动物们的当务之急是设法武装起来,并自我训练使用武器。而按斯诺鲍的说法,他们应该放出越来越多的鸽子,到其他庄园的动物中煽动造反。一个说如不自卫就无异于坐以待毙;另一个则说如果造反四起,他们就断无自卫的必要。动物们先听了拿破仑的,又听了斯诺鲍的,竟不能确定谁是谁非。实际上,他们总是发现,讲话的是谁,他们就会同意谁的。
26 |
27 | 终于熬到了这一天,斯诺鲍的设计图完成了。在紧接着的星期天大会议上,是否开工建造风车的议题将要付诸表决,当动物们在大谷仓里集合完毕,斯诺鲍站了起来,尽管不时被羊的咩咩声打断,他还是提出了他热衷于建造风车的缘由。接着,拿破仑站起来反驳,他非常隐讳地说风车是瞎折腾,劝告大家不要支持它,就又猛地坐了下去。他斤斤讲了不到半分钟,似乎显得有点说不说都一个样。这时,斯诺鲍跳了起来,喝住了又要咩咩乱叫的羊,慷慨陈词,呼吁大家对风车给予支持。在这之前,动物们因各有所好,基本上是平均地分成两派,但在顷刻之间,斯诺鲍的雄辩口才就说得他们服服贴贴。他用热烈的语言,描述着当动物们摆脱了沉重的劳动时动物庄园的景象。他的设想此时早已远远超出了铡草机和切萝卜机。他说,电能带动脱粒机、犁、耙、碾子、收割机和捆扎机,除此之外,还能给每一个窝棚里提供电灯、热水或凉水,以及电炉等等。他讲演完后,表决会何去何从已经很明显了。就在这个关头,拿破仑站起来,怪模怪样地瞥了斯诺鲍一眼,把了一声尖细的口哨,这样的口哨声以前没有一个动物听到他打过。
28 |
29 | 这时,从外面传来一阵凶狠的汪汪叫声,紧接着,九条强壮的狗,戴着镶有青铜饰钉的项圈,跳进大仓谷里来,径直扑向斯诺鲍。就在斯诺鲍要被咬上的最后一刻,他才跳起来,一下跑到门外,于是狗就在后面追。动物们都吓呆了,个个张口结舌。他们挤到门外注视着这场追逐。斯诺鲍飞奔着穿过通向大路的牧场,他使出浑身解数拼命地跑着。而狗已经接近他的后蹄子。突然间,他滑倒了,眼看着就要被他们逮住。可他又重新起来,跑得更快了。狗又一次赶上去,其中一条狗几乎就要咬住斯诺鲍的尾巴了,幸而斯诺鲍及时甩开了尾巴。接着他又一个冲刺,和狗不过一步之差,从树篱中的一个缺口窜了出去,再也看不到了。
30 |
31 | 动物们惊愕地爬回大谷仓。不一会儿,那些狗又汪汪地叫着跑回来。刚开始时,动物们都想不出这些家伙是从哪儿来的,但问题很快就弄明白了:他们正是早先被拿破仑从他们的母亲身边带走的那些狗崽子,被拿破仑偷偷地养着。他们尽管还没有完全长大,但个头都不小,看上去凶得象狼。大家都注意到,他们始终紧挨着拿破仑,对他摆着尾巴。那姿势,竟和别的狗过去对琼斯先生的做法一模一样。
32 |
33 | 这时,拿破仑在狗的尾随下,登上那个当年麦哲发表演讲的凸台,并宣布,从今以后,星期天早晨的大会议就此告终。他说,那些会议毫无必要,又浪费时间。此后一切有关庄园工作的议题,将有一个由猪组成的特别委员会定夺,这个委员会由他亲自统管。他们将在私下碰头,然后把有关决策传达给其他动物。动物们仍要在星期天早晨集合,向庄园的旗帜致敬,唱“英格兰兽”,并接受下一周的工作任务。但再也不搞什么辩论了。
34 |
35 | 本来,斯诺鲍被逐已经对他们刺激不小了,但他们更为这个通告感到惊愕。有几个动物想要抗议,却可惜没有找到合适的辩词。甚至鲍克瑟也感到茫然不解,他支起耳朵,抖动几下额毛,费力地想理出个头绪,结果没想出任何可说的话。然而,有些猪倒十分清醒,四只在前排的小肉猪不以为然地尖声叫着,当即都跳起来准备发言。但突然间,围坐在拿破仑身旁的那群狗发出一阵阴森恐怖的咆哮,于是,他们便沉默不语,重新坐了下去。接着,羊又声音响亮地咩咩叫起“四条腿好,两条腿坏!”一直持续了一刻钟,从而,所有讨论一下的希望也付诸东流了。
36 |
37 | 后来,斯奎拉受命在庄园里兜了一圈,就这个新的安排向动物作一解释。
38 |
39 | “同志们”,他说,“我希望每一位在这儿的动物,会对拿破仑同志为承担这些额外的劳动所作的牺牲而感激的。同志们,不要以为当领导是一种享受!恰恰相反,它是一项艰深而繁重的职责。没有谁能比拿破仑同志更坚信所有动物一律平等。他也确实很想让大家自己为自己作主。可是,万一你们失策了,那么同志们,我们会怎样呢?要是你们决定按斯诺鲍的风车梦想跟从了他会怎样呢?斯诺鲍这家伙,就我们现在所知,不比一个坏蛋强多少。”
40 |
41 | “他在牛棚大战中作战很勇敢”,有个动物说了一句。
42 |
43 | “勇敢是不够的”,斯奎拉说,“忠诚和服从更为重要。就牛棚大战而言,我相信我们最终会有一天发现斯诺鲍的作用被吹得太大了。纪律,同志们,铁的纪律!这是我们今天的口号。一步走错,我们的仇敌便会来颠覆我们。同志们,你们肯定不想让琼斯回来吧?”
44 |
45 | 这番论证同样是无可辩驳的。毫无疑问,动物们害怕琼斯回来;如果星期天早晨召集的辩论有导致他回来的可能,那么辩论就应该停止。鲍克瑟细细琢磨了好一阵子,说了句“如果这是拿破仑同志说,那就一定没错”,以此来表达他的整个感受。并且从此以后,他又用“拿破仑同志永远正确”这句格言,作为对他个人的座右铭“我要更加努力工作”的补充。
46 |
47 | 到了天气变暖,春耕已经开始的时候。那间斯诺鲍用来画风车设计图的小棚还一直被封着,大家想象着那些设计图早已从地板上擦掉了。每星期天早晨十点钟,动物们聚集在大谷仓,接受他们下一周的工作任务。如今,老麦哲的那个风干了肉的颅骨,也已经从果园脚下挖了出来,驾在旗杆下的一个木墩上,位于枪的一侧。升旗之后,动物们要按规定恭恭敬敬地列队经过那个颅骨,然后才走进大谷仓。近来,他们还没有像早先那样全坐在一起过。拿破仑同斯奎拉和另一个叫梅尼缪斯的猪,共同坐在前台。这个梅尼缪斯具有非凡的天赋,擅于谱曲作诗。九条年轻的狗围着它们成半圆形坐着。其他猪坐在后台。别的动物面对着他们坐在大谷仓中间。拿破仑用一种粗暴的军人风格,宣读对下一周的安排,随后只唱了一遍“英格兰兽”,所有的动物就解散了。
48 |
49 | 斯诺鲍被逐后的第三个星期天,拿破仑宣布要建造风车,动物们听到这个消息,终究有些吃惊。而拿破仑没有为改变主意讲述任何理由,只是简单地告诫动物们,那项额外的任务将意味着非常艰苦的劳动:也许有必要缩减他们的食料。然而,设计图已全部筹备好,并已经进入最后的细节部分。一个由猪组成的特别委员会为此在过去三周内一直工作着。风车的修建,加上其他一些各种各样的改进,预期要两年时间。
50 |
51 | 当天晚上,斯奎拉私下对其他动物解释说,拿破仑从来没有真正反对过风车。相反,正是由他最初做的建议。那个斯诺鲍画在孵卵棚地板上的设计图,实际上是他早先从拿破仑的笔记中剽窃的。事实上,风车是拿破仑自己的创造。于是,有的动物问道,为什么他曾说它的坏话说得那么厉害?在这一点上,斯奎拉显得非常圆滑。他说,这是拿破仑同志的老练,他装作反对风车,那只是一个计谋,目的在于驱除斯诺鲍这个隐患,这个坏东西。既然现在斯诺鲍已经溜掉了,计划也就能在没有斯诺鲍妨碍的情况下顺利进行了。斯奎拉说,这就是所谓的策略,他重复了好几遍,“策略,同志们,策略!”还一边带着欢快的笑声,一边甩动着尾巴,活蹦乱跳。动物们吃不准这些话的含意,可是斯奎拉讲的如此富有说服力,加上赶巧了有三条狗和他在一起,又是那样气势汹汹的狂叫着,因而他们没有进一步再问什么,就接受了他的解释。
52 |
53 | (感谢中译者张毅、高孝先以及本书电子版的输入者复旦大学的唐薇小姐)
--------------------------------------------------------------------------------
/第4章/program/动物农场/ 第八章.txt:
--------------------------------------------------------------------------------
1 |
2 |
3 | 几天以后,这次行刑引起的恐慌已经平息下来后,有些动物才想起了第六条诫律中已经规定:“任何动物不得伤害其他动物”,至少他们自以为记得有这条规定。尽管在提起这个话题时,谁也不愿让猪和狗听见,但他们还是觉得这次杀戮与这一条诫律不相符。克拉弗请求本杰明给她念一下第六条诫律,而本杰明却像往常一样说他不愿介入这类事情。她又找来穆丽尔。穆丽尔就给她念了,上面写着:“任何动物不得伤害其他动物而无缘无故”。对后面这五个字,动物们不知怎么回事就是不记得了。但他们现在却清楚地看到,杀掉那些与斯诺鲍串通一气的叛徒是有充分根据的,它并没有违犯诫律。
4 |
5 | 整整这一年,动物们比前些年干得更加卖力。重建风车,不但要把墙筑得比上一次厚一倍,还要按预定日期完成;再加上庄园里那些日常性活计,这两项合在一起,任务十分繁重。对动物来说,他们已经不止一次感觉到,现在干活时间比琼斯时期长,吃得却并不比那时强。每到星期天早上,斯奎拉蹄子上就捏着一张长纸条,向他们发布各类食物产量增加的一系列数据,根据内容分门别类,有的增加了百分之二百,有的增加了百分之三百或者百分之五百。动物们觉得没有任何理由不相信他,尤其是因为他们再也记不清楚起义前的情形到底是什么样了。不过,他们常常觉得,宁愿要这些数字少一些,而吃得更多些。
6 |
7 | 现在所有的命令都是通过斯奎拉,或者另外一头猪发布的。拿破仑自己则两星期也难得露一次面。一旦他要出来了,他就不仅要带着狗侍卫,而且还要有一只黑色小公鸡,象号手一样在前面开道。在拿破仑讲话之前,公鸡先要响亮地啼叫一下“喔——喔——喔!”据说,这是在庄主院,拿破仑也和别的猪分开居住的。用他在两头狗的侍侯下独自用餐,而且还总要德贝陶瓷餐具用餐,那些餐具原来陈列在客厅的玻璃橱柜里。另外,有通告说,每年逢拿破仑生日也要鸣枪,就向其他两个纪念日一样。
8 |
9 | 如今,对拿破仑给不能简单地直呼“拿破仑”了。提到他就要用正式的尊称:“我们的领袖拿破仑同志”,而那些猪还喜欢给他冠以这样一些头衔,如“动物之父”,“人类克星”,“的羊保护神”,“鸭子的至亲”等等。斯奎拉每次演讲时,总要泪流满面地大谈一番拿破仑的智慧和他的好心肠,说他对普天之下的动物,尤其是对那些还不幸地生活在其它庄园里的受歧视和受奴役的动物,满怀着深挚的爱等等。在庄园里,把每遇到一件幸运之事,每取得一项成就的荣誉归于拿破仑已成了家常便饭。你会常常听到一只鸡对另一只鸡这样讲道:“在我们的领袖拿破仑的指引下,我在六天之内下了五只蛋”,或者两头正在饮水的牛声称:“多亏拿破仑同志的领导,这水喝起来真甜!”庄园里的动物们的整个精神状态,充分体现在一首名为“拿破仑同志”的诗中,诗是梅尼缪斯编写的,全诗如下:
10 |
11 | 孤儿之至亲!
12 |
13 | 辛福之源泉!
14 |
15 | 赐给食料的的恩主!
16 |
17 | 您双目坚毅沉静
18 |
19 | 如日当空,
20 |
21 | 仰着看您
22 |
23 | 啊!我满怀激情
24 |
25 | 拿破仑同志!
26 |
27 | 是您赐予
28 |
29 | 您那众生灵所期求之一切,
30 |
31 | 每日两餐饱食,
32 |
33 | 还有那洁净的草垫,
34 |
35 | 每个动物不论大小,
36 |
37 | 都在窝棚中平静歇睡,
38 |
39 | 因为有您在照看,
40 |
41 | 拿破仑同志!
42 |
43 | 我要是有头幼崽,
44 |
45 | 在他长大以前,
46 |
47 | 哪怕他小得像奶瓶、像小桶,
48 |
49 | 他也应学会
50 |
51 | 用忠诚和老实待您,
52 |
53 | 放心吧,
54 |
55 | 他的第一声尖叫肯定是
56 |
57 | “拿破仑同志!”
58 |
59 | 拿破仑对这首诗很满意,并让手下把它刻在大谷仓的墙上,位于与“七诫”相对的另一头。诗的上方是拿破仑的一幅侧身画像,是斯奎拉用白漆画成的。
60 |
61 | 在这期间,由温普尔牵线,拿破仑正着手与弗雷德里克及皮尔金顿进行一系列繁冗的谈判。那堆木材至今还没有卖掉。在这两个人中,弗雷德里克更急着要买,但他又不愿意出一个公道的价钱。与此同时,有一个过时的消息重新开始流传,说弗雷德里克和他的伙计们正在密谋袭击动物庄园,并想把那个他嫉恨已久的风车毁掉,据说斯诺鲍就藏在平彻菲尔德庄园。仲夏时节,动物们又惊讶地听说,另外有三只鸡也主动坦白交待,说他们曾受斯诺鲍的煽动,参与过一起刺杀拿破仑的阴谋。那三只鸡立即被处决了,随后,为了拿破仑的安全起见,又采取了新的戒备措施,夜间有四条狗守卫着他的床,每个床脚一条狗,一头名叫平克埃的猪,接受了在拿破仑吃饭前品尝他的食物的任务,以防食物有毒。
62 |
63 | 差不多同时,有通知说拿破仑决定把那堆木材卖给皮尔金顿先生;他还拟订一项关于动物庄园和福克斯伍德庄园交换某些产品的长期协议。尽管是通过温普尔牵线,但拿破仑和皮尔金顿现在的关系可以说是相当不错的。对于皮尔金顿这个人,动物们并不信任。但他们更不信任弗雷德里克,他们对他又怕又恨。夏天过去了,风车即将竣工,那个关于弗雷德里克将要袭击庄园的风声也越来越紧。据说危险已经迫在眉睫,而且,弗雷德里克打算带二十个全副武装的人来,还说他已经买通了地方官员和警察,这样,一旦他能把动物庄园的地契弄到手,就会得到他们的认可。更有甚者,从平彻菲尔德庄园透露出许多可怕的消息,说弗雷德里克正用他的动物进行残酷无情的演习。他用鞭子抽死了一匹老马,饿他的牛,还把一条狗扔到炉子里烧死了,到了晚上,他就把刮脸刀碎片绑在鸡爪子上看斗鸡取乐。听到这些正加害在他们同志身上的事,动物们群情激愤,热血沸腾,他们不时叫嚷着要一起去进攻平彻菲尔德庄园,赶走那里的人,解放那里的动物。但斯奎拉告诫动物们,要避免草率行动,要相信拿破仑的战略布署。
64 |
65 | 尽管如此,反对弗雷德里克的情绪还是越来越高涨。在一个星期天早上,拿破仑来到大谷仓,他解释说他从来未打算把那堆木料卖给弗雷德里克。他说,和那个恶棍打交道有辱他的身份。为了向外传播起义消息而放出去的鸽子,以后不准在福克斯伍德庄园落脚。他还下令,把他们以前的口号“打倒人类”换成“打倒弗雷德里克”。夏末,斯诺鲍的另一个阴谋又被揭露了,麦田里长满了杂草,原来发现是他在某个夜晚潜入庄园后,往粮种里拌了草籽。一只与此事件有牵连的雄鸡向斯奎拉坦白了这一罪行,随后,他就吞食了剧毒草莓自尽了。动物们现在还得知,和他们一直想像的情况正相反,斯诺鲍从来都没有受到过“一级动物英雄”嘉奖。受奖的事只不过是在牛棚大战后,斯诺鲍自己散布的一个神话。根本就没有给他授勋这回事,倒是因为他在战斗中表现怯懦而早就受到谴责。有些动物又一次感到不好接受,但斯奎拉很快就使他们相信是他们记错了。
66 |
67 | 到了秋天,动物们在保证完成收割的情况下,竭尽全力,终于使风车竣工了,而且几乎是和收割同时完成的。接下来还得安装机器,温普尔正在为购买机器的事而奔忙,但是到此为止,风车主体已经建成。且不说他们经历的每一步如何困难,不管他们的经验多么不足,工具多么原始,运气多么不佳,斯诺鲍的诡计多么阴险,整个工程到此已经一丝不差按时竣工了!动物们精疲力尽,但却倍感自豪,他们绕着他们自己的这一杰作不停地转来转去。在他们眼里,风车比第一次筑得漂亮多了,另外,墙座也比第一次的厚一倍。这一次,除了炸药,什么东西都休想摧毁它们!回想起来,他们为此不知流过多少血和汗,又克服了不知多少个困难,但是一想到一旦当风车的翼板转动就能带动发电机,就会给他们的生活带来巨大的改观,——想到这前前后后的一切,他们于是就忘却了疲劳,而且还一边得意地狂呼着,一边围着风车雀跃不已。拿破仑在狗和公鸡的前呼后拥下,亲自莅临视察,并亲自对动物们的成功表示祝贺,还宣布,这个风车要命名为“拿破仑风车”。
68 |
69 | 两天后,动物们被召集到大谷仓召开一次特别会议。拿破仑宣布,他已经把那堆木料卖给了弗雷德里克,再过一天,弗雷德里克就要来拉货。顿时,动物们一个个都惊得目瞪口呆。在整个这段时间里,拿破仑只是与皮尔金顿表面上友好而已,实际上他已和弗雷德里克达成了秘密协议。
70 |
71 | 与福克斯伍德庄园的关系已经完全破裂了,他们就向皮尔金顿发出了侮辱信,并通知鸽子以后要避开平彻菲尔德庄园,还把“打倒弗雷德里克”的口号改为“打倒皮尔金顿”。同时,拿破仑断然地告诉动物们说,所谓动物庄园面临着一个迫在眉睫的袭击的说法是彻头彻尾的谎言,还有,有关弗雷德里克虐待他的动物的谣传,也是被严重地夸张了的。所有的谣言都极可能来自斯诺鲍及其同伙。总之,现在看来斯诺鲍并没有藏在平彻菲尔德庄园。事实上他生平从来没有到过那儿,他正住在福克斯伍德庄园,据说生活得相当奢侈。而且多年来,他一直就是皮尔金顿门下的一个地地道道的食客。
72 |
73 | 猪无不为拿破仑的老练欣喜若狂。他表面上与皮尔金顿友好,这就迫使弗雷德里克把价钱提高了十二英镑。斯奎拉说,拿破仑思想上的卓越之处,实际上就体现在他对任何人都不信任上,即使对弗雷德里克也是如此。弗雷德里克曾打算用一种叫做支票的东西支付木料钱,那玩意儿差不多只是一张纸,只不过写着保证支付之类的诺言而已,但拿破仑根本不是他能糊弄得了的,他要求用真正的五英镑票子付款,而且要在运木料之前交付。弗雷德里克已经如数付清,所付的数目刚好够为大风车买机器用。
74 |
75 | 这期间,木料很快就被拉走了,等全部拉完之后,在大谷仓里又召开了一次特别会议,让动物们观赏弗雷德里克付给的钞票。拿破仑笑逐颜开,心花怒放,他戴着他的两枚勋章,端坐在那个凸出的草垫子上,钱就在他身边,整齐地堆放在从庄主院厨房里拿来的瓷盘子上。动物们排成一行慢慢走过,无不大饱眼福。鲍克瑟还伸出鼻子嗅了嗅那钞票,随着他的呼吸,还激起了一股稀稀的白末屑和嘶嘶作响声。
76 |
77 | 三天以后,在一阵震耳的嘈杂声中,只见温普尔骑着自行车飞快赶来,面色如死人一般苍白。他把自行车在院子里就地一扔,就径直冲进庄主院。过来一会,就在拿破仑的房间里响起一阵哽噎着嗓子的怒吼声。出事了,这消息象野火一般传遍整个庄园。钞票是假的!弗雷德里克白白地拉走了木料!
78 |
79 | 拿破仑立即把所有动物召集在一起,咬牙切齿地宣布,判处弗雷德里克死刑。他说,要是抓住这家伙,就要把他活活煮死。同时他告诫他们,继这个阴险的背信弃义的行动之后,最糟糕的事情也就会一触即发了。弗雷德里克和他的同伙随时都可能发动他们蓄谋已久的袭击。因此,已在所有通向庄园的路口安装了岗哨。另外,四只鸽子给福克斯伍德庄园送去和好的信件,希望与皮尔金顿重修旧好。
80 |
81 | 就在第二天早晨,敌人开始袭击了。当时动物们正在吃早饭,哨兵飞奔来报,说弗雷德里克及其随从已经走进了五栅门。动物们勇气十足,立刻就向敌人迎头出击,但这一回他们可没有像牛棚大战那样轻易取胜。敌方这一次共有十五个人,六条枪,他们一走到距离五十码处就立刻开火。可怕的枪声和恶毒的子弹使动物们无法抵挡,虽然拿破仑和鲍克瑟好不容易才把他们集结起来,可不一会儿他们就又被打退了回来。很多动物已经负伤。于是他们纷纷逃进庄园的窝棚里躲了起来,小心翼翼地透过墙缝,透过木板上的节疤孔往外窥探。只见整个大牧场,还有风车,都已落到敌人手中。此时就连拿破仑似乎也已不知所措了。他一言不发,走来走去,尾巴变得僵硬,而且还不停抽搐着。他不时朝着福克斯伍德庄园方向瞥去渴望的眼光。如果皮尔金顿和他手下的人帮他们一把的话,这场拼斗还可以打胜。但正在此刻,前一天派出的四只鸽子返回来了,其中有一只带着皮尔金顿的一张小纸片。纸上用铅笔写着:“你们活该。”
82 |
83 | 这时,弗雷德里克一伙人已停在风车周围。动物们一边窥视着他们,一边惶恐不安地嘀咕起来,有两个人拿出一根钢钎和一把大铁锤,他们准备拆除风车。
84 |
85 | “不可能!”拿破仑喊道,“我们已把墙筑得那么厚。他们休想在一星期内拆除。不要怕,同志们!”
86 |
87 | 但本杰明仍在急切地注视着那些人的活动。拿着钢钎和大铁锤的两个人,正在风车的地基附近打孔。最后,本杰明带着几乎是戏谑的神情,慢腾腾地呶了呶他那长长的嘴巴。
88 |
89 | “我看是这样”他说,“你们没看见他们在干什么吗?过一会儿,他们就要往打好的孔里装炸药。”
90 |
91 | 太可怕了。但此时此刻,动物们不敢冒险冲出窝棚,他们只好等待着。过了几分钟,眼看着那些人朝四下散开,接着,就是一声震耳欲聋的爆炸声。顿时,鸽子就立刻飞到空中,其它动物,除了拿破仑外,全都转过脸去,猛地趴倒在地。他们起来后,风车上空飘荡着一团巨大的黑色烟云。微风慢慢吹散了烟云:风车已荡然无存!
92 |
93 | 看到这情景,动物们又重新鼓起勇气。他们在片刻之前所感到的胆怯和恐惧,此刻便被这种可耻卑鄙的行为所激起的狂怒淹没了。他们发出一阵强烈的复仇呐喊,不等下一步的命令,便一齐向敌人冲去。这一次,他们顾不上留意那如冰雹一般扫射而来的残忍的子弹了。这是一场残酷、激烈的战斗。那帮人在不断地射击,等到动物们接近他们时,他们就又用棍棒和那沉重的靴子大打出手。一头牛、三只羊、两只鹅被杀害了,几乎每个动物都受了伤。就连一直在后面指挥作战的拿破仑也被子弹削去了尾巴尖。但人也并非没有伤亡。三个人的头被鲍克瑟的蹄掌打破;另一个人的肚子被一头牛的犄角刺破;还有一个人,裤子几乎被杰西和布鲁拜尔撕掉,给拿破仑作贴身警卫的那九条狗,奉他的命令在树篱的遮掩下迂回过去,突然出现在敌人的侧翼,凶猛地吼叫起来,把那帮人吓坏了。他们发现有被包围的危险,弗雷德里克趁退路未断便喊他的同伙撤出去,不一会儿,那些贪生怕死的敌人便没命似地逃了。动物们一直把他们追到庄园边上,在他们从那片树篱中挤出去时,还踢了他们最后几下。
94 |
95 | 他们胜利了,但他们都已是疲惫不堪,鲜血淋漓。它们一瘸一拐地朝庄园缓缓地走回。看到横在草地上的同志们的尸体,有的动物悲伤得眼泪汪汪。他们在那个曾矗立着风车的地方肃穆地站了好长时间。的的确确,风车没了;他们劳动的最后一点印迹几乎也没了!甚至地基也有一部分被炸毁,而且这一下,要想再建风车,也非同上一次可比了。上一次还可以利用剩下的石头。可这一次连石头也不见了。爆炸的威力把石头抛到了几百码以外。好像这儿从未有过风车一样。
96 |
97 | 当他们走近庄园,斯奎拉朝他们蹦蹦跳跳地走过来,他一直莫名其妙地没有参加战斗,而此时却高兴得摇头摆尾。就在这时,动物们听到从庄园的窝棚那边传来祭典的鸣枪声。
98 |
99 | “干嘛要开枪?”鲍克瑟问。
100 |
101 | “庆祝我们的胜利!”斯奎拉囔道。
102 |
103 | “什么胜利?”鲍克瑟问。他的膝盖还在流血,又丢了一只蹄铁,蹄子也绽裂了,另外还有十二颗子弹击中了他的后腿。
104 |
105 | “什么胜利?同志们,难道我们没有从我们的领土上——从神圣的动物庄园的领土上赶跑敌人吗?”
106 |
107 | “但他们毁了风车,而我们却为建风车干了两年!”
108 |
109 | “那有什么?我们将另建一座。我们高兴的话就建它六座风车。同志们,你们不了解,我们已经干了一件多么了不起的事。敌人曾占领了我们脚下这块土地。而现在呢,多亏拿破仑同志的领导,我们重新夺回了每一-土地!”
110 |
111 | “然而我们夺回的只是我们本来就有的,”鲍克瑟又说道。
112 |
113 | “这就是我们的胜利,”斯奎拉说。
114 |
115 | 他们一瘸一拐地走进大院。鲍克瑟腿皮下的子弹使他疼痛难忍。他知道,摆在他面前的工作,将是一项从地基开始再建风车的沉重劳动,他还想像他自己已经为这项任务振作了起来。但是,他第一次想到,他已十一岁了。他那强壮的肌体也许是今非昔比了。
116 |
117 | 但当动物们看到那面绿旗在飘扬,听到再次鸣枪——共响了七下,听到拿破仑的讲话,听到他对他们的行动的祝贺,他们似乎觉得,归根到底,他们取得了巨大的胜利。大家为在战斗中死难的动物安排了一个隆重的葬礼。鲍克瑟和克拉弗拉着灵车,拿破仑亲自走在队列的前头。整整两天用来举行庆祝活动,有唱歌,有演讲,还少不了鸣枪,每一个牲口都得了一只作为特殊纪念物的苹果,每只家禽得到了二盎司谷子,每条狗有三块饼干。有通知说,这场战斗将命名为风车战役,拿破仑还设立了一个新勋章“绿旗勋章”,并授予了他自己。在这一片欢天喜地之中,那个不幸的钞票事件也就被忘掉了。
118 |
119 | 庆祝活动过后几天,猪偶然在庄主院的地下室里,发现了一箱威士忌,这在他们刚住进这里时没注意到。当天晚上,从庄主院那边传出一阵响亮的歌声,令动物们惊奇的是,中间还夹杂着“英格兰兽”的旋律。大约在九点半左右,只见拿破仑戴着一顶琼斯先生的旧圆顶礼帽,从后门出来,在院子里飞快地跑了一圈,又闪进门不见了。但在第二天早晨,庄主院内却是一片沉寂,看不到一头猪走动,快到九点钟时,斯奎拉出来了,迟缓而沮丧地走着,目光呆滞,尾巴无力地掉在身后,浑身上下病怏怏的。他把动物们叫到一起,说还要传达一个沉痛的消息:拿破仑同志病危!
120 |
121 | 一阵哀嚎油然而起。庄主院门外铺着草甸,于是,动物们踮着蹄尖从那儿走过。他们眼中含着热泪,相互之间总是询问:要是他们的领袖拿破仑离开了,他们可该怎么办。庄园里此刻到处都在风传,说斯诺鲍最终还是设法把毒药掺到拿破仑的食物中了。十一点,斯奎拉出来发布另一项公告,说是拿破仑同志在弥留之际宣布了一项神圣的法令:饮酒者要处死刑。
122 |
123 | 可是到了傍晚,拿破仑显得有些好转,次日早上,斯奎拉就告诉他们说拿破仑正在顺利康复。即日夜晚,拿破仑又重新开始工作了。又过了一天,动物们才知道,他早先让温普尔在威灵顿买了一些有关蒸馏及酿造酒类方面的小册子。一周后,拿破仑下令,叫把苹果园那边的小牧场耕锄掉。那牧场原先是打算为退休动物留作草场用的,现在却说牧草已耗尽,需要重新耕种;但不久以后便真相大白了,拿破仑准备在那儿播种大麦。
124 |
125 | 大概就在这时,发生了一件奇怪的事情,几乎每个动物都百思不得其解。这事发生在一天夜里十二点钟左右,当时,院子里传来一声巨大的跌撞声,动物们都立刻冲出窝棚去看。那个夜晚月光皎洁,在大谷仓一头写着“七诫”的墙角下,横着一架断为两截的梯子。斯奎拉平躺在梯子边上,一时昏迷不醒。他手边有一盏马灯,一把漆刷子,一只打翻的白漆桶。狗当即就把斯奎拉围了起来,待他刚刚苏醒过来,马上就护送他回到了庄主院。除了本杰明以外,动物们都想不通这是怎么回事。本杰明呶了呶他那长嘴巴,露出一副会意了的神情,似乎看出点眉目来了,但却啥也没说。
126 |
127 | 但是几天后,穆丽尔自己在看到七诫时注意到,又有另外一条诫律动物们都记错了,他们本来以为,第五条诫律是“任何动物不得饮酒”,但有两个字他们都忘了,实际上那条诫律是“任何动物不得饮酒过度”。
128 |
129 | (感谢中译者张毅、高孝先以及本书电子版的输入者复旦大学的唐薇小姐)
--------------------------------------------------------------------------------
/第4章/program/动物农场/ 第六章.txt:
--------------------------------------------------------------------------------
1 |
2 |
3 | 那一年,动物们干起活来就像奴隶一样。但他们乐在其中,流血流汗甚至牺牲也心甘情愿,因为他们深深地意识到:他们干的每件事都是为他们自己和未来的同类的利益,而不是为了那帮游手好闲、偷摸成性的人类。
4 |
5 | 从初春到夏末这段时间里,他们每周工作六十个小时。到了八月,拿破仑又宣布,星期天下午也要安排工作。这项工作完全是自愿性的,不过,无论哪个动物缺勤,他的口粮就要减去一半。即使这样,大家还是发觉,有些活就是干不完。收获比去年要差一些,而且,因为耕作没有及早完成,本来应该在初夏播种薯类作物的两快地也没种成。可以预见,来冬将是一个艰难的季节。
6 |
7 | 风车的事引起了意外的难题。按说,庄园里就有一个质地很好的石灰石矿,又在一间小屋里发现了大量的沙子和水泥,这样,所有的建筑材料都已齐备。但问题是,动物们刚开始不知道如何才能把石头弄碎到适用的规格。似乎除了动用十字镐和撬棍外,没有别的办法。可是,动物们都不能用后腿站立,也就无法使用镐和撬棍。在他们徒劳几个星期之后,才有动物想出了一个好主意,就是利用重力的作用。再看那些巨大的圆石,虽然大都无法直接利用,但整个采石场上到处都是。于是,动物们用绳子绑住石头,然后,由牛、马、羊以及所有能抓住绳子的动物合在一起——甚至猪有时也在关键时刻搭个帮手——一起拖着石头,慢慢地、慢慢地沿着坡拖到矿顶。到了那儿,把石头从边上堆下去,在底下就摔成了碎块。这样一来,运送的事倒显得相对简一些了。马驾着满载的货车运送,羊则一块一块地拖,就连穆丽尔和本杰明也套上一辆旧两轮座车,贡献出了他们的力量。这样到了夏末,备用的石头便积累足了,接着,在猪的监督下,工程就破土动工了。
8 |
9 | 但是,整个采石过程在当时却进展缓慢,历尽艰辛。把一块圆石拖到矿顶,常常要竭尽全力干整整一天,有些时候,石头从崖上推下去了,却没有摔碎。要是没有鲍克瑟,没有他那几乎能与所有其他动物合在一起相匹敌的力气,恐怕什么事都干不成。每逢动物们发现圆石开始往下滑,他们自己正被拖下山坡而绝望地哭喊时,总是多亏鲍克瑟拉住了绳索才稳了下来。看着他蹄子尖紧扣着地面,一-一-吃力地爬着坡;看着他呼吸急促,巨大的身躯浸透了汗水,动物们无不满怀钦佩和赞叹。克拉弗常常告诫他小心点,不要劳累过度了,但他从不放在心上。对他来说,“我要更加努力工作”和“拿破仑同志永远正确”这两句口头禅足以回答所有的难题。他已同那只小公鸡商量好了,把原来每天早晨提前半小时叫醒他,改为提前三刻钟。同时,尽管近来业余时间并不多,但他仍要在空闲时间里,独自到采石场去,在没有任何帮手的情况下,装上一车碎石,拖去倒在风车的地基里。
10 |
11 | 这一夏季,尽管动物们工作得十分辛苦,他们的境况还不算太坏,虽然他们得到的饲料不比琼斯时期多,但至少也不比那时少。除了自己食用外,动物们不必去并供养那五个骄奢淫逸的人,这个优越性太显著了,它足以使许多不足之处显得不足为道。另外,动物们干活的方式,在许多情况下,不但效率高而且省力。比如锄草这类活,动物们可以干得完美无缺,而对人来说,这一点远远做不到。再说,如今的动物们都不偷不摸了,也就不必用篱笆把牧场和田地隔开,因此便省去了大量的维护树篱和栅栏的劳力。话虽如此,过了夏季,各种各样意料不到的缺欠就暴露出来了。庄园里需要煤油、钉子、线绳、狗食饼干以及马蹄上钉的铁掌等等,但庄园里又不出产这些东西。后来,又需要种子和人造化肥,还有各类工具以及风车用的机。可是,如何搞到这些东西,动物们就都想像不出了。
12 |
13 | 一个星期天早晨,当动物们集合起来接受任务时,拿破仑宣布,他已经决定了一项新政策。说是往后动物庄园将要同邻近的庄园做些交易,这当然不是为了任何商业目的,而是仅仅为了获得某些急需的物资。他说,为风车所需要的东西一定要不惜一切代价。因此,他正在准备出卖一堆干草和和当年的部分小麦收成,而且,再往后如果需要更多的钱的话,就得靠卖鸡蛋来补充了,因为鸡蛋在威灵顿总是有销路的。拿破仑还说,鸡应该高兴地看到,这一牺牲就是他们对建造风车的特殊贡献。
14 |
15 | 动物们再一次感到一种说不出的别扭。决不和人打交道,决不从事交易,决不使用钱,这些最早就有的誓言,在琼斯被逐后的第一次大会议上,不就已经确立了吗?订立这些誓言的情形至今都还历历在目;或者至少他们自以为还记得有这回事。那四只曾在拿破仑宣布废除大会议时提出抗议的幼猪胆怯地发言了,但在狗那可怕的咆哮声下,很快又不吱声了。接着,羊又照例咩咩地叫起“四条腿好,两条腿坏!”一时间的难堪局面也就顺利地对付过去了。最后,拿破仑抬起前蹄,平静一下气氛,宣布说他已经作好了全部安排,任何动物都不必介入和人打交道这种明显最为讨厌的事体中。而他有意把全部重担放在自己肩上。一个住在威灵顿的叫温普尔先生的律师,已经同意担当动物庄园和外部社会的中介人,并且将在每个星期一早晨来访以接受任务。最后,拿破仑照例喊一声:“动物庄园万岁!”就结束了整个讲话。接着,动物们在唱完“英格兰兽”后,纷纷散场离去。
16 |
17 | 后来,斯奎拉在庄园里转了一圈才使动物们安心下来。他向他们打保票说,反对从事交易和用钱的誓言从来没有通过过,搞不好连提议都不曾有过。这纯粹是臆想,追溯其根源,很可能是斯诺鲍散布的一个谎言。对此,一些动物还是半信半疑,斯奎拉就狡黠问他们:“你们敢肯定这不是你们梦到一些事吗?同志们!你们有任何关于这个誓约的记录吗?它写在哪儿了?”自然,这类东西都从没有见诸文字。因此,动物们便相信是他们自己搞错了。
18 |
19 | 那个温普尔是个律师,长着络腮胡子,矮个子,看上去一脸奸诈相。他经办的业务规模很小,但他却精明过人,早就看出了动物庄园会需要经纪人,并且佣金会很可观的。按协议,每个星期一温普尔都要来庄园一趟。动物们看着他来来去去,犹有几分畏惧,避之唯恐不及。不过,在他们这些四条腿的动物看来,拿破仑向靠两条腿站着的温普尔发号施令的情景,激发了他们的自豪,这在一定程度上也让他们感到这个新协议是顺心的。现在,他们同人类的关系确实今非昔比了。但是,人们对动物庄园的嫉恨不但没有因为它的兴旺而有所消解,反而恨之弥深。而且每个人都怀着这样一个信条:动物庄园迟早要破产,并且关键是,那个风车将是一堆废虚。他们在小酒店聚会,相互用图表论证说风车注定要倒塌;或者说,即便它能建成,那也永远运转不起来云云。虽然如此,他们对动物们管理自己庄园能力,也不由自主地刮目相看了。其中一个迹象就是,他们在称呼动物庄园时,不再故意叫它曼纳庄园,而开始用动物庄园这个名正言顺的名称。他们放弃了对琼斯的支持,而琼斯自己也已是万念俱焚,不再对重主他的庄园抱有希望,并且已经移居到国外另一个地方了。如今,多亏了这个温普尔,动物庄园才得以和外部社会接触,但是不断有小道消息说,拿破仑正准备同福克斯伍德的皮尔金顿先生,或者是平彻菲尔德的弗雷德里克先生签订一项明确的商业协议,不过还提到,这个协议永远不会同时和两家签订的。
20 |
21 | 大概就是在这个时候,猪突然搬进了庄主院,并且住在那里了。这一下,动物们又似乎想起了,有一条早先就立下的誓愿是反对这样做的。可斯奎拉又教他们认识到,事实并非如此。他说,猪是庄园的首脑,应该有一个安静的工作场所,这一点绝对必要。再说,对领袖(近来他在谈到拿破仑时,已经开始用“领袖”这一尊称)的尊严来说,住在房屋里要比住在纯粹的猪圈里更相称一些。尽管这样,在一听到猪不但在厨房里用餐,而且把客厅当作娱乐室占用了之后,还是有一些动物为此深感不安。鲍克瑟到蛮不在乎,照例说了一句“拿破仑同志永远正确。”但是克拉弗却认为她记得有一条反对床铺的诫律,她跑到大谷仓那里,试图从题写在那儿的“七诫”中找出答案。结果发现她自己连单个的字母都不认不过来。她便找来穆丽尔。
22 |
23 | “穆丽尔”她说道,“你给我念一下第四条诫律,它是不是说决不睡在床上什么的?”
24 |
25 | 穆丽尔好不容易才拼读出来。
26 |
27 | “它说,‘任何动物不得卧床铺盖被褥’,”她终于念道。
28 |
29 | 克拉弗觉得太突兀了,她从不记得第四条诫律提到过被褥,可它既然就写在墙上,那它一定本来就是这样。赶巧这时候,斯奎拉在两三条狗的陪伴下路过这儿,他能从特殊的角度来说明整个问题。
30 |
31 | “那么,同志们,你们已经听到我们猪现在睡到庄主院床上的事了?为什么不呢?你们不想想,真的有过什么诫律反对床吗?床只不过是指一个睡觉的地方。如果正确看待的话,窝棚里的稻草堆就是一张床。这条诫律是反对被褥的,因为被褥是人类发明的。我们已经把庄主院床上的被褥全撤掉了,而睡在毯子里。它们也是多么舒服的床啊!可是同志们,我可以告诉你们,现在所有的脑力工作得靠我们来做,和我们所需要的程度相比,这些东西并不见得舒服多少。同志们,你们不会不让我们休息吧?你们不愿使我们过于劳累而失职吧?肯定你们谁都不愿意看到琼斯回来吧?”
32 |
33 | 在这一点上,动物们立刻就使他消除了疑虑,也不再说什么有关猪睡在庄主院床上的事了。而且数日之后,当宣布说,往后猪的起床时间要比其他动物晚一小时,也没有谁对此抱怨。
34 |
35 | 直到秋天,动物们都挺累的,却也愉快。说起来他们已经在艰难中熬过整整一年了,并且在卖了部分干草和玉米之后,准备过冬的饲料就根本不够用了,但是,风车补偿这一切,它这时差不多建到一半了。秋收以后,天气一直晴朗无雨,动物们干起活来比以前更勤快了。他们整天拖着石块,辛劳地来回奔忙。他们想着这样一来,便能在一天之内把墙又加高一-了,因而是多么富有意义啊!鲍克瑟甚至在夜间也要出来,借着中秋的月光干上一两个小时。动物们则乐于在工余时间绕着进行了一半的工程走来走去,对于那墙壁的强度和垂直度赞叹一番。并为他们竟能修建如此了不起的工程而感到惊喜交加。唯独老本杰明对风车毫无热情,他如同往常一样,除了说驴都长寿这句话神乎其神的话之外,就再也无所表示了。
36 |
37 | 十二月到了,带来了猛烈的西北风。这时常常是雨天,没法和水泥,建造工程不得不中断。后来有一个夜晚,狂风大作,整个庄园里的窝棚从地基上都被摇撼了,大谷仓顶棚的一些瓦片也刮掉了。鸡群在恐惧中嘎嘎乱叫着惊醒来,因为他们在睡梦中同时听见远处在打枪。早晨,动物们走出窝棚,发现旗杆已被风吹倒,果园边上的一棵榆树也象萝卜一样被连根拔起。就在这个时候,所有的动物喉咙里突然爆发出一阵绝望的哭喊。一幅可怕的景象呈现在他们面前:风车毁了。
38 |
39 | 他们不约而同地冲向现场。很少外出散步的拿破仑,率先跑在最前头。是的,他们的全部奋斗成果躺在那儿了,全部夷为平地了,他们好不容易弄碎又拉来的石头四下散乱着。动物们心酸地凝视着倒塌下来的碎石块,一下子说不出话来。拿破仑默默地来回踱着步,偶尔在地面上闻一闻,他的尾巴变得僵硬,并且还忽左忽右急剧地抽动,对他来说,这是紧张思维活动的表现。突然,他不动了,似乎心里已有了主意。
40 |
41 | “同志们,”他平静地说,“你们知道这是谁做的孽吗?那个昨晚来毁了我们风车的仇敌你们认识吗?斯诺鲍!”他突然用雷鸣般的嗓音吼道:“这是斯诺鲍干的!这个叛徒用心何其毒也,他摸黑爬到这儿,毁了我们近一年的劳动成果。他企图借此阻挠我们的计划,并为他可耻的被逐报复。同志们,此时此刻,我宣布判处斯诺鲍死刑。并给任何对他依法惩处的动物授予‘二级动物英雄’勋章和半莆式耳苹果,活捉他的动物将得到一整莆式耳苹果。”
42 |
43 | 动物们得知斯诺鲍竟能犯下如此罪行,无不感到十分愤慨。于是,他们在一阵怒吼之后,就开始想象如何在斯诺鲍再回来时捉住他。差不多就在同时,在离小山包不远的草地上,发现了猪蹄印。那些蹄印只能跟踪出几步远,但看上去是朝着树篱缺口方向的。拿破仑对着蹄印仔细地嗅了一番,便一口咬定那蹄印是斯诺鲍的,他个人认为斯诺鲍有可能是从福克斯伍德庄园方向来的。
44 |
45 | “不要再迟疑了,同志们!”拿破仑在查看了蹄印后说道:“还有工作要干,我们正是要从今天早晨起,开始重建风车,而且经过这个冬天,我们要把它建成。风雨无阻。我们要让这个卑鄙的叛徒知道,他不能就这样轻而易举地破坏我们的工作。记住,同志们,我们的计划不仅不会有任何变更,反而要一丝不苟地实行下去。前进,同志们!风车万岁!动物庄园万岁!”
46 |
47 | (感谢中译者张毅、高孝先以及本书电子版的输入者复旦大学的唐薇小姐)
--------------------------------------------------------------------------------
/第4章/program/动物农场/ 第十章.txt:
--------------------------------------------------------------------------------
1 |
2 |
3 | 春去秋来,年复一年。随着岁月的流逝,寿命较短的动物都已相继死去。眼下,除了克拉弗、本杰明、乌鸦摩西和一些猪之外,已经没有一个能记得起义前的日子了。
4 |
5 | 穆丽尔死了,布鲁拜尔、杰西、平彻尔都死了,琼斯也死了,他死在国内其他一个地方的一个酒鬼家里。斯诺鲍被忘掉了。鲍克瑟也被忘掉了,所不同的是,唯有几个本来就相识的动物还记得。克拉弗如今也老了,她身体肥胖,关节僵硬,眼里总带着一团眼屎。按退休年龄来说,她的年龄已超过两年了,但实际上,从未有一个动物真正退休。拨出大牧场一角给退休动物享用的话题也早就搁到一边了。如今的拿破仑已是一头完全成熟的雄猪,体重三百多磅。斯奎拉胖得连睁眼往外看都似乎感到困难。只有老本杰明,几乎和过去一个样,就是鼻子和嘴周围有点发灰,再有一点,自从鲍克瑟死去后,他比以前更加孤僻和沉默寡言。
6 |
7 | 现在,庄园里的牲口比以前多得多了,尽管增长的数目不象早些年所预见的那么大。很多动物生在庄园,还有一些则来自别的地方。对于那些出生在庄园的动物来说,起义只不过是一个朦朦胧胧的口头上的传说而已;而对那些来自外乡的动物来说,他们在来到庄园之前,还从未听说过起义的事。现在的庄园,除了克拉弗之外,另外还有三匹马,他们都是好同志,都很了不起,也都十分温顺,可惜反应都很慢。看起来,他们中间没有一个能学会字母表上“B”以后的字母。对于有关起义和动物主义原则的事,凡是他们能听到的,他们都毫无保留地全盘接受,尤其是对出自克拉弗之口的更是如此。他们对克拉弗的尊敬,已近乎于孝顺。但是,他们究竟是不是能弄通这些道理,仍然值得怀疑。
8 |
9 | 现在的庄园更是欣欣向荣,也更是井然有序了。庄园里增加了两块地,这两块地是从皮尔金顿先生那里买来的。风车最终还是成功地建成了,庄园里也有了自己的一台打谷机及草料升降机。另外,还加盖了许多种类不一的新建筑。温普尔也为自己买了一辆双轮单驾马车。不过,风车最终没有用来发电,而是用来磨谷子啦,并且为庄园创收了数目可观的利润。如今,动物们又为建造另一座风车而辛勤劳作,据说,等这一座建成了,就要安装上发电机。但是,当年谈论风车时,斯诺鲍引导动物们所想像的那种享受不尽的舒适,那种带电灯和冷热水的窝棚,那种每周三天工作制,如今不再谈论了。拿破仑早就斥责说,这些想法是与动物主义的精神背道而驰的。他说,最纯粹的幸福在于工作勤奋和生活俭朴。
10 |
11 | 不知道为什么,反正看上去,庄园似乎已经变得富裕了,但动物们自己一点没有变富,当然猪和狗要排除在外。也许,其中的部分原因是由于猪和狗都多吧。处在他们这一等级的动物,都是用他们自己的方式从事劳动。正像斯奎拉乐于解释的那样,在庄园的监督和组织工作中,有很多没完没了的事,在这类事情中,有大量工作是其它动物由于无知而无法理解的。例如,斯奎拉告诉他们说,猪每天要耗费大量的精力,用来处理所谓“文件”、“报告”、“会议记录”和“备忘录”等等神秘的事宜。这类文件数量很大,还必须仔细填写,而且一旦填写完毕,又得把它们在炉子里烧掉。斯奎拉说,这是为了庄园的幸福所做的最重要的工作。但是至今为止,无论是猪还是狗,都还没有亲自生产过一粒粮食,而他们仍然为数众多,他们的食欲还总是十分旺盛。
12 |
13 | 至于其它动物,迄今就他们所知,他们的生活还是一如既往。他们普遍都在挨饿,睡的是草垫,喝的是池塘里的水,干的是田间里的活,冬天被寒冷所困,夏天又换成了苍蝇。有时,他们中间的年长者绞尽脑汁,竭尽全力从那些淡漠的印象中搜索着回忆的线索,他们试图以此来推定起义后的早期,刚赶走琼斯那会,情况是比现在好呢还是糟,但他们都记不得了。没有一件事情可以用来和现在的生活做比较,除了斯奎拉的一系列数字以外,他们没有任何凭据用来比较,而斯奎拉的数字总是千篇一律地表明,所有的事正变得越来越好。动物们发现这个问题解释不清,不管怎么说,他们现在很少有时间去思索这类事情。唯有老本杰明与众不同,他自称对自己那漫长的一生中的每个细节都记忆犹新,还说他认识到事物过去没有,将来也不会有什么更好或更糟之分。因此他说,饥饿、艰难、失望的现实,是生活不可改变的规律。
14 |
15 | 不过,动物们仍然没有放弃希望。确切地说,他们身为动物庄园的一员,从来没有失去自己的荣誉感和优越感,哪怕是一瞬间也没有过。他们的庄园依然是整个国家——所有英伦三岛中——唯一的归动物所有、并由动物管理的庄园。他们中间的成员,就连最年轻的,甚至还有那些来自十英里或二十英里以外庄园的新成员,每每想到这一点,都无不感到惊喜交加。当他们听到鸣枪,看到旗杆上的绿旗飘扬,他们内心就充满了不朽的自豪,话题一转,也就时常提起那史诗般的过去,以及驱除琼斯、刻写“七诫”、击退人类来犯者的伟大战斗等等。那些旧日的梦想一个也没有丢弃。想当年麦哲预言过的“动物共和国”,和那个英格兰的绿色田野上不再有人类足迹践踏的时代,至今依然是他们信仰所在。他们依然相信:总有一天,那个时代会到来,也许它不会马上到来,也许它不会在任何现在健在的动物的有生之年到来,但它终究要到来。而且至今,说不定就连“英格兰兽”的曲子还在被到处偷偷得哼唱着,反正事实上,庄园里的每个动物都知道它,尽管谁也不敢放声大唱。也许,他们生活艰难;也许,他们的希望并没有全部实现,但他们很清楚,他们和别的动物不一样。如果他们还没有吃饱,那么也不是因为把食物拿去喂了暴虐的人类;如果他们干活苦了,那么至少他们是在为自己辛劳。在他们中间,谁也不用两条腿走路,谁也不把谁称做“老爷”,所有动物一律平等。
16 |
17 | 初夏的一天,斯奎拉让羊跟着他出去,他把他们领到庄园的另一头,那地方是一块长满桦树苗的荒地。在斯奎拉的监督下,羊在那里吃了整整一天树叶子,到了晚上,斯奎拉告诉羊说,既然天气暖和了,他们就呆在那儿算了。然后,他自己返回了庄主院。羊在那里呆了整整一个星期。在这期间,别的动物连他们的一丝影子也没见着。斯奎拉每天倒是耗费大量时间和他们泡在一起。他解释说,他正在给他们教唱一首新歌,因此十分需要清静。
18 |
19 | 那是一个爽朗的傍晚,羊回来了。当时,动物们才刚刚收工,正走在回窝棚的路上。突然,从大院里传来了一声马的悲鸣,动物们吓了一跳,全都立即停下脚步。是克拉弗的声音,她又嘶叫起来。于是,所有的动物全都奔跑着冲进了大院。这一下,他们看到了克拉弗看到的情景。
20 |
21 | 是一头猪在用后腿走路。
22 |
23 | 是的,是斯奎拉。他还有点笨拙好象还不大习惯用这种姿势支撑他那巨大的身体,但他却能以熟练的平衡,在院子里散步了。不大一会,从庄主院门里又走出一长队猪,都用后腿在行走。他们走到好坏不一,有一两头猪还有点不稳当,看上去好像他们本来更适于找一根棍子支撑着。不过,每头猪都绕着院子走得相当成功。最后,在一阵非常响亮的狗叫声和那只黑公鸡尖细的啼叫声中,拿破仑亲自走出来了,他大模大样地直立着,眼睛四下里轻慢地瞥了一下。他的狗则活蹦乱跳地簇拥再他的周围。
24 |
25 | 他蹄子中捏着一根鞭子。
26 |
27 | 一阵死一般的寂静。惊讶、恐惧的动物们挤在一堆,看着那一长溜猪慢慢地绕着院子行走。仿佛这世界已经完全颠倒了。接着,当他们从这场震惊中缓过一点劲的时候,有那么一瞬间,他们顾不上顾虑任何事——顾不上他们对狗的害怕,顾不上他们多少年来养成的,无论发生什么事,他们也从来不抱怨、从批评的习惯——他们马上要大声抗议了,但就在这时,象是被一个信号激了一下一样,所有的羊爆发出一阵巨大的咩咩声——
28 |
29 | “四条腿好,两条腿更好!四条腿好,两条腿更好!四条腿好,两条腿更好!”
30 |
31 | 喊叫声不间歇地持续了五分钟。等羊安静下来后,已经错过了任何抗议的机会了,因为猪已列队走回庄主院。
32 |
33 | 本杰明感觉到有一个鼻子在他肩上磨蹭。回头一看,是克拉弗。只见她那一双衰劳的眼睛比以往更加灰暗。她没说一句话,轻轻地拽他的鬃毛,领着他转到大谷仓那一头,那儿是写着“七诫”的地方。他们站在那里注视着有白色字体的柏油墙,足有一两分钟。
34 |
35 | “我的眼睛不行了”,他终于说话了,“就是年轻时,我也认不得那上面所写的东西。可是今天,怎么我看这面墙不同以前了。‘七诫’还是过去那样吗?本杰明?”
36 |
37 | 只有这一次,本杰明答应破个例,他把墙上写的东西念给她听,而今那上面已经没有别的什么了,只有一条诫律,它是这样写的:
38 |
39 | 所有动物一例平等
40 |
41 | 但有些动物比其他动物
42 |
43 | 更加平等
44 |
45 | 从此以后,似乎不再有什么可稀奇的了:第二天所有的猪在庄园监督干活时蹄子上都捏着一根鞭子,算不得稀奇;猪给他们自己买一台无线电收音机,并正在准备安装一部电话,算不得稀奇;得知他们已经订阅了《约翰-牛报》、《珍闻报》及《每日镜报》,算不得稀奇;看到拿破仑在庄主院花园里散步时,嘴里含着一根烟斗,也算不得稀奇。是的,不必再大惊小怪了。哪怕猪把琼斯先生的衣服从衣柜里拿出来穿在身上也没有什么。如今,拿破仑已经亲自穿上了一件黑外套和一条特制的马裤,还绑上了皮绑腿,同时,他心爱的母猪则穿上一件波纹绸裙子,那裙子是琼斯夫人过去常在星期天穿的。
46 |
47 | 一周后的一天下午,一位两轮单驾马车驶进庄园。一个由邻近庄园主组成的代表团,已接受邀请来此进行考查观光。他们参观了整个庄园,并对他们看到的每件事都赞不绝口,尤其是对风车。那时,动物们正在萝卜地里除草,他们干得细心认真,很少扬起脸,搞不清他们是对猪更害怕呢,还是对来参观的人更害怕。
48 |
49 | 那天晚上,从庄主院里传来一阵阵哄笑声和歌声。动物们突然被这混杂的声音吸引住了。他们感到好奇的是,既然这是动物和人第一次在平等关系下济济一堂,那么在那里会发生什么事呢?于是他们便不约而同地,尽量不出一点声音地往庄主院的花园里爬去。
50 |
51 | 到了门口,他们又停住了,大概是因为害怕而不敢再往前走,但克拉弗带头进去了,他们踮着蹄子,走到房子跟前,那些个头很高的动物就从餐厅的窗户上往里面看。屋子里面,在那张长长的桌子周围,坐着六个庄园主和六头最有名望的猪,拿破仑自己坐在桌子上首的东道主席位上,猪在椅子上显出一副舒适自在的样子。宾主一直都在津津有味地玩扑克牌,但是在中间停了一会,显然是为了准备干杯。有一个很大的罐子在他们中间传来传去,杯子里又添满了啤酒。他们都没注意到窗户上有很多诧异的面孔正在凝视着里面。
52 |
53 | 福克斯伍德庄园的皮尔金顿先生举着杯子站了起来。他说道,稍等片刻,他要请在场的诸位干杯。在此之前,他感到有几句话得先讲一下。
54 |
55 | 他说,他相信,他还有其他在场的各位都感到十分喜悦的是,持续已久的猜疑和误解时代已经结束了。曾有这样一个时期,无论是他自己,还是在座的诸君,都没有今天这种感受,当时,可敬的动物庄园的所有者,曾受到他们的人类邻居的关注,他情愿说这关注多半是出于一定程度上的焦虑,而不是带着敌意。不幸的事件曾发生过,错误的观念也曾流行过。一个由猪所有并由猪管理经营的庄园也曾让人觉得有些名不正言不顺,而且有容易给邻近庄园带来扰乱因素的可能。相当多的庄园主没有做适当的调查就信口推断说,在这样的庄园里,肯定会有一种放荡不羁的歪风邪气在到处蔓延。他们担心这种状况会影响到他们自己的动物,甚至影响他们的雇员。但现在,所有这种怀疑都已烟消云散了。今天,他和他的朋友们拜访了动物庄园,用他们自己的眼睛观察了庄园的每一个角落。他们发现了什么呢?这里不仅有最先进的方法,而且纪律严明,有条不紊,这应该是各地庄园主学习的榜样。他相信,他有把握说,动物庄园的下级动物,比全国任何动物干的活都多,吃的饭都少。的确,他和他的代表团成员今天看到了很多有特色之处,他们准备立即把这些东西引进到他们各自的庄园中去。
56 |
57 | 他说,他愿在结束发言的时候,再次重申动物庄园及其邻居之间已经建立的和应该建立的友好感情。在猪和人之间不存在,也不应该存在任何意义上的利害冲突。他们的奋斗目标和遇到的困难是一致的。劳工问题不是到处都相同嘛?讲到这里,显然,皮尔金顿先生想突然讲出一句经过仔细琢磨的妙语,但他好一会儿乐不可支,讲不出话来,他竭力抑制住,下巴都憋得发紫了,最后才蹦出一句:“如果你们有你们的下层动物在作对,”他说,“我们有我们的下层阶级!”这一句意味隽永的话引起一阵哄堂大笑。皮尔金顿先生再次为他在动物庄园看到的饲料供给少、劳动时间长,普遍没有娇生惯养的现象等等向猪表示祝贺。
58 |
59 | 他最后说道,到此为止,他要请各位站起来,实实在在地斟满酒杯。“先生们,”皮尔金顿先生在结束时说,“先生们,我敬你们一杯:为动物庄园的繁荣昌盛干杯!”
60 |
61 | 一片热烈的喝彩声和跺脚声响起。拿破仑顿时心花怒放,他离开座位,绕着桌子走向皮尔金顿先生,和他碰了杯便喝干了,喝采声一静下来,依然靠后腿站立着的拿破仑示意,他也有几句话要讲。
62 |
63 | 这个讲话就象拿破仑所有的演讲一样,简明扼要而又一针见血。他说,他也为那个误解的时代的结束而感到高兴。曾经有很长一个时期,流传着这样的谣言,他有理由认为,这些谣言是一些居心叵测的仇敌散布的,说在他和他的同僚的观念中,有一种主张颠覆、甚至是从根本上属于破坏性的东西。他们一直被看作是企图煽动邻近庄园的动物造反。但是,事实是任何谣言都掩盖不了的。他们唯一的愿望,无论是在过去还是现在,都是与他们的邻居和平共处,保持正常的贸易关系。他补充说,他有幸掌管的这个庄园是一家合营企业。他自己手中的那张地契,归猪共同所有。
64 |
65 | 他说道,他相信任何旧的猜疑不会继续存在下去了。而最近对庄园的惯例又作了一些修正,会进一步增强这一信心。长期以来,庄园里的动物还有一个颇为愚蠢的习惯,那就是互相以“同志”相称。这要取消。还有一个怪僻,搞不清是怎么来的,就是在每个星期天早上,要列队走过花园里一个钉在木桩上的雄猪头盖骨。这个也要取消。头盖骨已经埋了。他的来访者也许已经看到那面旗杆上飘扬着的绿旗。果然如此的话,他们或许已经注意到,过去旗面上画着的白色蹄掌和犄角现在没有了。从今以后那面旗将是全绿的旗。
66 |
67 | 他说,皮尔金顿先生的精采而友好的演讲,他只有一点要作一补充修正。皮尔金顿先生一直提到“动物庄园”,他当然不知道了,因为就连他拿破仑也只是第一次宣告,“动物庄园”这个名字作废了。今后,庄园的名字将是“曼纳庄园”,他相信,这个名字才是它的真名和原名。
68 |
69 | “先生们,“他总结说,“我将给你们以同样的祝辞,但要以不同的形式,请满上这一杯。先生们,这就是我的祝辞:为曼纳庄园的繁荣昌盛干杯!”
70 |
71 | 一阵同样热烈而真诚的喝采声响起,酒也一饮而尽。但当外面的动物们目不转睛地看着这一情景时,他们似乎看到了,有一些怪事正在发生。猪的面孔上发生了什么变化呢?克拉弗那一双衰老昏花的眼睛扫过一个接一个面孔。他们有的有五个下巴,有的有四个,有的有三个,但是有什么东西似乎正在融化消失,正在发生变化。接着,热烈的掌声结束了,他们又拿起扑克牌,继续刚才中断的游戏,外面的动物悄悄地离开了。
72 |
73 | 但他们还没有走出二十码,又突然停住了。庄主院传出一阵吵闹声。他们跑回去,又一次透过窗子往里面看。是的,里面正在大吵大闹。那情景,既有大喊大叫的,也有捶打桌子的;一边是疑神疑鬼的锐利的目光,另一边却在咆哮着矢口否认。动乱的原因好象是因为拿破仑和皮尔金顿先生同时打出了一张黑桃A。
74 |
75 | 十二个嗓门一齐在愤怒地狂叫着,他们何其相似乃尔!而今,不必再问猪的面孔上发生了什么变化。外面的众生灵从猪看到人,又从人看到猪,再从猪看到人;但他们已分不出谁是猪,谁是人了。
76 |
77 | 1943年11月——1944年2月
78 |
79 | (感谢中译者张毅、高孝先以及本书电子版的输入者复旦大学的唐薇小姐)
--------------------------------------------------------------------------------
/第4章/program/动物农场/ 第四章.txt:
--------------------------------------------------------------------------------
1 |
2 |
3 | 到了那里夏末,有关动物庄园里种种事件的消息,已经传遍了半个国家。每一天,斯诺鲍和拿破仑都要放出一群鸽子。鸽子的任务是混入附近庄园的动物中,告诉他们起义的史实,教他们唱“英格兰兽”。
4 |
5 | 这个时期,琼斯先生把大部分时间都在泡在威灵顿雷德兰的酒吧间了。他心怀着被区区畜牲撵出家园的痛苦,每逢有人愿意听,他就诉说一通他的冤屈。别的庄园主基本上同情他,但起初没有给他太多帮助。他们都在心里暗暗寻思,看是否能多少从琼斯的不幸中给自己捞到什么好处。幸而,与动物庄园毗邻的两个庄园关系一直很差。一个叫作福克斯伍德庄园,面积不小,却照管得很差。广阔的田地里尽是荒芜的牧场和丢人现眼的树篱。庄园主皮尔金顿先生是一位随和的乡绅,随着季节不同,他不是钓鱼消闲,就是去打猎度日。另一个叫作平彻菲尔德庄园,小一点,但照料得不错。它的主人是弗雷德里克先生,一个精明的硬汉子,却总是牵扯在官司中,落了个好斤斤计较的名声。这两个人向来不和,谁也不买谁的帐,即使事关他们的共同利益,他们也是如此。
6 |
7 | 话虽如此,可是这一次,他们俩都被动物庄园的造反行动彻底吓坏了,急不可待地要对他们自己庄园里的动物封锁这方面的消息。开始的时候,他们对动物们自己管理庄园的想法故作嘲笑与蔑视。他们说,整个事态两周内就会结束。他们散布说,曼纳庄园(他们坚持称之为曼纳庄园,而不能容忍动物庄园这个名字)的畜牲总是在他们自己之间打斗,而且快要饿死了。过一段时间,那里的动物显然并没有饿死,弗雷德里克和皮尔金顿就改了腔调,开始说什么动物庄园如今邪恶猖獗。他们说,传说那里的动物同类相食,互相用烧得通红的马蹄铁拷打折磨,还共同霸占他们中的雌性动物。弗雷德里克和皮尔金顿说,正是在这一点上,造反是悖于天理的。
8 |
9 | 然而,谁也没有完全听信这些说法。有这样一座奇妙的庄园,在那儿人被撵走,动物们掌管自己的事务,这个小道消息继续以各种形式流传着。整个那一年,在全国范围内造反之波此起彼伏:一向温顺的公牛突然变野了,羊毁坏了树篱,糟踏了苜蓿,母牛蹄翻了奶桶,猎马不肯越过围栏而把背上的骑手甩到了另一边。更有甚者,“英格兰兽”的曲子甚至还有歌词已经无处不知,它以惊异的速度流传着。尽管人们故意装作不屑一顾,认为它滑稽可笑,但是,当他们听到了这支歌,便怒不可遏。他们说,他们简直弄不明白,怎么就连畜牲们也竟能唱这样无耻的下流小调。那些因为唱这支歌而被逮住的动物,当场就会被责以鞭笞。可这支歌还是压抑不住的,乌鸦在树篱上啭鸣着唱它,鸽子在榆树上咕咕着唱它,歌声渗进铁匠铺的喧声,渗进教堂的钟声,它预示着人所面临的厄运,因而,他们听到这些便暗自发抖。
10 |
11 | 十月初,玉米收割完毕并且堆放好了,其中有些已经脱了粒。有一天,一群鸽子从空中急速飞回,兴高采烈地落在动物庄园的院子里。原来琼斯和他的所有伙计们,以及另外六个来自福克斯伍德庄园和平彻菲尔德庄园的人,已经进了五栅门,正沿着庄园的车道向这走来。除了一马当先的琼斯先生手里握着一支枪外,他们全都带着棍棒。显然,他们企图夺回这座庄园。
12 |
13 | 这是早就预料到了的,所有相应的准备工作也已经就绪。斯诺鲍负责这次防御战。他曾在庄主院的屋子里找到一本谈论儒略-凯撒征战的旧书,并且钻研过。此时,他迅速下令,不出两分钟,动物们已经各就各位。
14 |
15 | 当这伙人接近庄园的窝棚时,斯诺鲍发动第一次攻击,所有的鸽子,大概有三十五只左右,在这伙人头上盘旋,从半空中向他们一齐拉屎。趁着他们应付鸽子的“空袭”,早已藏在树篱后的一群鹅冲了出来,使劲地啄他们的腿肚子。而这还只是些小打小闹的计策,只不过制造点小混乱罢了。这帮人用棍棒毫不费力就把鹅赶跑了。斯诺鲍接着发动第二次攻击,穆丽尔、本杰明和所有的羊,随着打头的斯诺鲍冲向前去,从各个方向对这伙人又戳又抵,而本杰明则回头用他的小蹄子对他们尥起蹶子来。可是,对动物们来说,这帮拎着棍棒、靴子上又带着钉子的人还是太厉害了。突然,从斯诺鲍那里发出一声尖叫,这是退兵的信号,所有的动物转身从门口退回院子内。
16 |
17 | 那些人发出得意的呼叫,正象他们所想象的那样,他们看到仇敌们溃不成军,于是就毫无秩序的追击着。这正是斯诺鲍所期望的。等他们完全进入院子后,三匹马,三头牛以及其余埋伏在牛棚里的猪,突然出现在他们身后,切断了他们的退路。这时,斯诺鲍发出了进攻的信号,他自己径直向琼斯冲出,琼斯看见他冲过来,举起枪就开了火,弹粒擦过斯诺鲍背部,刻下了一道血痕,一只羊中弹伤亡。当时迟,那时快,斯诺鲍凭他那两百多磅体重猛地扑向琼斯的腿,琼斯一下子被推到粪堆上,枪也从手中甩了出去。而最为惊心动魄的情景还在鲍克瑟那儿,他就像一匹没有阉割的种马,竟靠后腿直立起来,用他那巨大的钉着铁掌的蹄子猛打一气,第一下就击中了一个福克斯伍德庄园的马夫的脑壳,打得他倒在泥坑里断了气。看到这个情形,几个人扔掉棍子就要跑。他们被惊恐笼罩着,接着,就在所有动物的追逐下绕着院子到处乱跑。他们不是被抵,就是被踢;不是被咬,就是被踩。庄园里的动物无不以各自不同的方式向他们复仇。就连那只猫也突然从房顶跳到一个放牛人的肩上,用爪子掐进他的脖子里,疼得他大喊大叫。趁着门口没有挡道的机会,这伙人喜出望外,夺路冲出院子,迅速逃到大路上。一路上又有鹅在啄着他们的腿肚子,嘘嘘的轰赶他们。就这样,他们这次侵袭,在五分钟之内,又从进来的路上灰溜溜地败逃了。
18 |
19 | 除了一个人之外,这帮人全都跑了。回到院子里,鲍克瑟用蹄子扒拉一下那个脸朝下趴在地上的马夫,试图把它翻过来,这家伙一动也不动。
20 |
21 | “他死了”,鲍克瑟难过地说,“我本不想这样干,我忘了我还钉着铁掌呢,谁相信我这是无意的呢?”
22 |
23 | “不要多愁善感,同志!”伤口还在滴滴答答流血的斯诺鲍大声说到。“打仗就是打仗,只有死人才是好人。”
24 |
25 | “我不想杀生,即使对人也不”,鲍克瑟重复道,两眼还含着泪花。
26 |
27 | 不知是谁大声喊道:“莫丽哪儿去了?”
28 |
29 | 莫丽确实失踪了。大家感到一阵惊慌,他们担心人设了什么计伤害了她,更担心人把她抢走了。结果,却发现她正躲在她的厩棚里,头还钻在料槽的草中。她在枪响的时候就逃跑了。后来又发现,那个马夫只不过昏了过去,就在他们寻找莫丽时,马夫苏醒过来,趁机溜掉了。
30 |
31 | 这时,动物们又重新集合起来,他们沉浸在无比的喜悦之中,每一位都扯着嗓子把自己在战斗中的功劳表白一番。当下,他们立即举行了一个即兴的庆功仪式。庄园的旗帜升上去了,“英格兰兽”唱了许多遍。接着又为那只被杀害的羊举行了隆重的葬礼,还为她在墓地上种了一棵山楂树。斯诺鲍在墓前作了一个简短的演说,他强调说,如果需要的话,每个动物都当为动物庄园准备牺牲。
32 |
33 | 动物们一致决定设立一个“一级动物英雄”军功勋章,这一称号就地立即授予斯诺鲍和鲍克瑟。并有一枚铜质奖章(那是在农具室里发现的一些旧的、货真价实的黄铜制做的),可在星期天和节日里佩戴。还有一枚“二级动物英雄”勋章,这一称号追认给那只死去的羊。
34 |
35 | 关于对这次战斗如何称谓的事,他们讨论来,讨论去,最后决定命名为“牛棚大战”,因为伏击就是在那儿发起的。他们还把琼斯先生那支掉在泥坑里的枪找到了,又在庄主院里发现了存贮的子弹。于是决定把枪架在旗杆脚下,像一门大炮一样,并在每年鸣枪两次,一次在十月十二日的“牛棚大战”纪念日,一次在施洗约翰节,也就是起义纪念日。
36 |
37 | (感谢中译者张毅、高孝先以及本书电子版的输入者复旦大学的唐薇小姐)
--------------------------------------------------------------------------------
/第5章/program/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/第5章/program/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/第5章/program/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/第5章/program/.idea/program.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/第5章/program/Chapter_5_bs4.py:
--------------------------------------------------------------------------------
1 | from bs4 import BeautifulSoup
2 | import requests
3 | import re
4 |
5 | html = requests.get('http://exercise.kingname.info/exercise_bs_1.html').content.decode()
6 |
7 | soup = BeautifulSoup(html, 'lxml')
8 |
9 | useful = soup.find(class_='useful')
10 | all_content = useful.find_all('li')
11 | for li in all_content:
12 | print(li['class'])
13 |
14 | content = soup.find_all(class_=re.compile('iam'))
15 | for each in content:
16 | print(each.string)
17 |
18 |
19 | useful = soup.find(class_='useful')
20 | all_content = useful.find_all('li')
21 | for li in all_content:
22 | print(li.string)
23 | print(li['class'])
24 |
25 | content = soup.find_all(text=re.compile('我需要'))
26 | for each in content:
27 | print(each.string)
28 |
29 | info_2 = soup.find(class_='test')
30 | print(f'使用find方法,返回的对象类型为:{type(info_2)}')
31 | print(info_2.string)
--------------------------------------------------------------------------------
/第5章/program/Chapter_5_damai.py:
--------------------------------------------------------------------------------
1 | import requests
2 | import lxml.html
3 | import csv
4 |
5 | url = 'https://www.damai.cn/projectlist.do'
6 | source = requests.get(url).content
7 |
8 | selector = lxml.html.fromstring(source)
9 | item_list = selector.xpath('//ul[@id="performList"]/li')
10 |
11 | item_dict_list = []
12 | for item in item_list:
13 | show_name = item.xpath('div[@class="ri-infos"]/h2/a/text()')
14 | show_url = item.xpath('div[@class="ri-infos"]/h2/a/@href')
15 | show_description = item.xpath('div[@class="ri-infos"]/p[1]/text()')
16 | show_time = item.xpath('div[@class="ri-infos"]/p[@class="mt5"]/text()')
17 | show_place = item.xpath('div[@class="ri-infos"]/p[@class="mt5"]/span[@class="ml20"]/a/text()')
18 | show_price = item.xpath('div[@class="ri-infos"]/p/span[@class="price-sort"]/text()')
19 |
20 | item_dict = {'show_name': show_name[0] if show_name else '',
21 | 'show_url': 'https:' + show_url[0] if show_url else '',
22 | 'show_description': show_description[0] if show_description else '',
23 | 'show_time': show_time[0].strip() if show_time else '',
24 | 'show_place': show_place[0] if show_place else '',
25 | 'show_price': show_price[0] if show_price else ''}
26 | item_dict_list.append(item_dict)
27 |
28 | with open('result.csv', 'w', encoding='utf-8') as f:
29 | writer = csv.DictWriter(f, fieldnames=['show_name',
30 | 'show_url',
31 | 'show_description',
32 | 'show_time',
33 | 'show_place',
34 | 'show_price'])
35 | writer.writeheader()
36 | writer.writerows(item_dict_list)
37 |
--------------------------------------------------------------------------------
/第5章/program/Chapter_5_tieba.py:
--------------------------------------------------------------------------------
1 | import requests
2 | import lxml.html
3 | source = requests.get('http://tieba.baidu.com/f?ie=utf-8&kw=%E5%91%A8%E6%9D%B0%E4%BC%A6&red_tag=g2896306195').content
4 | selector = lxml.html.fromstring(source)
5 | post_title_list = selector.xpath('//div[@class="threadlist_title pull_left j_th_tit "]/a/text()')
6 | for post_title in post_title_list:
7 | print(post_title)
8 |
--------------------------------------------------------------------------------
/第5章/program/Chapter_5_xpath.py:
--------------------------------------------------------------------------------
1 | import lxml.html
2 |
3 | source = '''
4 |
5 |
6 | 测试
7 |
8 |
9 |
10 |
11 | - 我需要的信息1
12 | - 我需要的信息2
13 | - 我需要的信息3
14 |
15 |
16 |
22 |
23 |
24 | '''
25 | selector = lxml.html.fromstring(source)
26 | # info_list = selector.xpath('//div[@class="useful"]/ul/li/text()')
27 | useful = selector.xpath('//div[@class="useful"]')
28 | info_list = useful[0].xpath('ul/li/text()')
29 | print(info_list)
30 |
--------------------------------------------------------------------------------
/第5章/program/Chapter_5_xpath_special.py:
--------------------------------------------------------------------------------
1 | import lxml.html
2 |
3 |
4 | html1 = '''
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | 需要的内容1
13 | 需要的内容2
14 | 需要的内容3
15 | 这是我不需要的内容
16 |
17 |
18 | '''
19 |
20 | # selector = lxml.html.fromstring(html1)
21 | # content = selector.xpath('//div[ends-with(@id, "-k")]/text()')
22 | # for each in content:
23 | # print(each)
24 |
25 | html2 = '''
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | 需要的内容1
34 | 需要的内容2
35 | 需要的内容3
36 | 这是我不需要的内容
37 |
38 |
39 | '''
40 |
41 | # selector = lxml.html.fromstring(html2)
42 | # content = selector.xpath('//div[contains(@id, "-key")]/text()')
43 | # for each in content:
44 | # print(each)
45 |
46 |
47 | html3 = '''
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | 我左青龙,
57 |
58 | 右白虎,
59 |
62 | 老牛在当中,
63 |
64 | 龙头在胸口。
65 |
66 |
67 |
68 | '''
69 | #如果使用一般的办法,就会出现获取到的数据不完整的情况
70 | selector = lxml.html.fromstring(html3)
71 | # content_1 = selector.xpath('//div[@id="test3"]/text()')
72 | # for each in content_1:
73 | # print(each)
74 |
75 | # 使用string(.)就可以把数据获取完整
76 | data = selector.xpath('//div[@id="test3"]')[0]
77 | info = data.xpath('string(.)')
78 | print(info)
79 |
--------------------------------------------------------------------------------
/第5章/program/result.csv:
--------------------------------------------------------------------------------
1 | show_name,show_url,show_description,show_time,show_place,show_price
2 | 薛之谦“我好像在哪见过你”-2017全国巡回演唱会广州站-统帅电器之夜,https://piao.damai.cn/118325.html,如果你曾听过他,如果你曾爱过那《认真的雪》,请和你爱的人一起来听他的演唱会吧!,时间:2017.06.03,广州国际体育演艺中心,321-1717
3 | “地表最强”2017周杰伦世界巡回演唱会 沈阳站,https://piao.damai.cn/117377.html,天马行空的创作思路使周杰伦的歌曲与任何一种音乐形式都不完全相同,形成极强的个人风格。,时间:2017.06.02-2017.06.03,沈阳奥林匹克体育中心体育场,380-1880
4 | “地表最强”2017周杰伦世界巡回演唱会 天津站,https://piao.damai.cn/118113.html,周杰伦天津演唱会精彩演出。,时间:2017.05.26-2017.05.27,天津奥体中心体育场,380-1880
5 | 爆笑零距离2.0,https://piao.damai.cn/119487.html,"喜剧,四个段子组成的喜剧,短!平!快!新!奇!怪!",时间:2017.03.18-2017.05.14,正华星博剧场,80-198
6 | 【A CLASSIC TOUR学友•经典】世界巡回演唱会 中山站,https://piao.damai.cn/122053.html,时隔近六年再度回到中山举行演唱会,而且是以四面台的形式演出,相信定能给观众带来新鲜感,时间:2017.06.09-2017.06.10,中山市兴中体育场,380-1680
7 | “青年晚报”-许嵩2017北京演唱会,https://piao.damai.cn/120705.html,演唱会精心筹备,力求回馈歌迷们的多年追随,还大家一个迟来的十年纪念。,时间:2017.06.03,乐视体育生态中心,180-1180
8 | 浦发银行信用卡呈献 薛之谦“我好像在哪见过你”—2017全国巡回演唱会-上海站,https://piao.damai.cn/122820.html,如果你曾听过他,如果你曾爱过那《认真的雪》,请和你爱的人一起来听他的演唱会吧!,时间:2017.06.10,上海梅赛德斯奔驰文化中心(上海世博文化中心),321-1717
9 | 薛之谦“我好像在哪见过你”—2017全国巡回演唱会青岛站,https://piao.damai.cn/121193.html,请和你爱的人一起来听他的演唱会吧!,时间:2017.05.20,青岛市体育中心国信体育馆,321-1717
10 | “地表最强”2017周杰伦世界巡回演唱会 南京站,https://piao.damai.cn/115862.html,“地表最强”2017周杰伦世界巡回演唱会 南京站,时间:2017.05.20-2017.05.21,南京奥体中心体育场,380-1980
11 | Live 4 LIVE 《尖叫现场》迪玛希“惊喜之夜”生日会,https://piao.damai.cn/122457.html,尖叫现场,点燃每一个有趣的灵魂。,时间:2017.05.24,北京展览馆剧场,380-1280
12 |
--------------------------------------------------------------------------------
/第6章/program/.idea/code.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/第6章/program/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/第6章/program/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/第6章/program/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/第6章/program/chapter_example_cqud.py:
--------------------------------------------------------------------------------
1 | from pymongo import MongoClient
2 | client = MongoClient()
3 | database = client['Chapter6']
4 | collection = database['spider']
5 |
6 | # 写入数据
7 | data = {'id': 123, 'name': 'kingname', 'age': 20, 'salary': 999999}
8 | collection.insert(data)
9 |
10 | more_data = [
11 | {'id': 2, 'name': '张三', 'age': 10, 'salary': 0},
12 | {'id': 3, 'name': '李四', 'age': 30, 'salary': -100},
13 | {'id': 4, 'name': '王五', 'age': 40, 'salary': 1000},
14 | {'id': 5, 'name': '外国人', 'age': 50, 'salary': '未知'},
15 | ]
16 |
17 | collection.insert(more_data)
18 |
19 | # 查询数据
20 | # content = [x for x in collection.find({'age': 29}, {'_id': 0, 'name': 1, 'salary': 1})]
21 | # content_obj = collection.find({'age': 29}, {'_id': 0, 'name': 1, 'salary': 1})
22 | # content = []
23 | # for each in content_obj:
24 | # content.append(each)
25 |
26 |
27 | # 逻辑查询
28 | # content = [x for x in collection.find({'age': {'$gte': 29, '$lte': 40}})]
29 |
30 | #排序
31 | # content = [x for x in collection.find({'age': {'$gte': 29, '$lte': 40}}).sort('age', -1)]
32 | # content = [x for x in collection.find({'age': {'$gte': 29, '$lte': 40}}).sort('age', 1)]
33 |
34 | # 去重
35 | # content = collection.distinct('age')
36 | # print('finished')
--------------------------------------------------------------------------------
/第6章/program/chapter_mongo_opt.py:
--------------------------------------------------------------------------------
1 | import pymongo
2 | import datetime
3 | import random
4 | import time
5 |
6 | connection = pymongo.MongoClient()
7 | db = connection.Chapter6
8 | handler_1_by_1 = db.Data_1_by_1
9 | handler_bat = db.Data_bat
10 |
11 | today = datetime.date.today()
12 |
13 | #逐条插入数据
14 | # start_1_by_1 = time.time()
15 | # for i in range(10000):
16 | # delta = datetime.timedelta(days=i)
17 | # fact_date = today - delta
18 | # handler_1_by_1.insert({'time': str(fact_date), 'data': random.randint(0, 10000)})
19 | # end_1_by_1 = time.time()
20 |
21 | #批量插入数据
22 | start_bat = time.time()
23 | insert_list = []
24 | for i in range(10000):
25 | delta = datetime.timedelta(days=i)
26 | fact_date = today - delta
27 | insert_list.append({'time': str(fact_date), 'data': random.randint(0, 10000)})
28 |
29 | handler_bat.insert(insert_list)
30 | end_bat = time.time()
31 |
32 | # print(f'一条一条插入数据,耗时:{end_1_by_1 - start_1_by_1}')
33 | print(f'批量插入数据,耗时: {end_bat - start_bat}')
34 |
--------------------------------------------------------------------------------
/第6章/program/chapter_update_opt.py:
--------------------------------------------------------------------------------
1 | import pymongo
2 | import datetime
3 | import time
4 |
5 | connection = pymongo.MongoClient()
6 | db = connection.Chapter6
7 | handler_bat = db.Data_bat
8 |
9 | start_1_by_1 = time.time()
10 | for row in handler_bat.find():
11 | old_date = row['time']
12 | old_time_datetime = datetime.datetime.strptime(old_date, '%Y-%m-%d')
13 | one_day = datetime.timedelta(days=1)
14 | new_date = old_time_datetime + one_day
15 | handler_bat.update({'_id': row['_id']}, {'$set': {'time': str(new_date.date())}}, upsert=False)
16 | end_1_by_1 = time.time()
17 |
18 | print(f'逐条更新数据,一共耗时:{end_1_by_1 - start_1_by_1}')
19 |
20 |
21 | # handler_update_2_insert = db.Data_update_2_insert
22 | # start_update_2_insert = time.time()
23 | # insert_list = []
24 | # for row in handler_bat.find():
25 | # old_date = row['time']
26 | # old_time_datetime = datetime.datetime.strptime(old_date, '%Y-%m-%d')
27 | # one_day = datetime.timedelta(days=1)
28 | # new_date = old_time_datetime + one_day
29 | # row['time'] = str(new_date.date())
30 | # insert_list.append(row)
31 | #
32 | # handler_update_2_insert.insert(insert_list)
33 | # end_update_2_insert = time.time()
34 | #
35 | # print(f'把更新变为插入,共耗时:{end_update_2_insert - start_update_2_insert}')
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/第6章/program/content_crawler.py:
--------------------------------------------------------------------------------
1 | import requests
2 | import pymongo
3 | import redis
4 | from lxml import html
5 |
6 | connection = pymongo.MongoClient()
7 | db = connection.Chapter6
8 | handler = db.white
9 |
10 | client = redis.StrictRedis()
11 |
12 | content_list = []
13 | while client.llen('url_queue') > 0:
14 | url = client.lpop('url_queue').decode()
15 | source = requests.get(url).content
16 |
17 | selector = html.fromstring(source)
18 | chapter_name = selector.xpath('//div[@class="h1title"]/h1/text()')[0]
19 | content = selector.xpath('//div[@id="htmlContent"]/p/text()')
20 | content_list.append({'title': chapter_name, 'content': '\n'.join(content)})
21 |
22 | handler.insert(content_list)
--------------------------------------------------------------------------------
/第6章/program/url_crawler.py:
--------------------------------------------------------------------------------
1 | import requests
2 | from lxml import html
3 | import redis
4 |
5 | client = redis.StrictRedis()
6 | source = requests.get('http://dongyeguiwu.zuopinj.com/5525').content
7 | selector = html.fromstring(source)
8 |
9 | url_list = selector.xpath('//div[@class="book_list"]/ul/li/a/@href')
10 | for url in url_list:
11 | client.lpush('url_queue', url)
--------------------------------------------------------------------------------
/第7章/program/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/第7章/program/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | $USER_HOME$/.subversion
6 | true
7 |
8 |
--------------------------------------------------------------------------------
/第7章/program/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/第7章/program/.idea/program.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/第7章/program/example_advanced.py:
--------------------------------------------------------------------------------
1 | import requests
2 | import json
3 | import time
4 |
5 |
6 | url = 'http://exercise.kingname.info/ajax_5_backend'
7 |
8 | html_json = requests.post(url,
9 | headers={'ReqTime': str(int(time.time() * 1000))},
10 | json={'sum': '6'}).content.decode()
11 | html_dict = json.loads(html_json)
12 |
13 | print(html_dict)
14 |
--------------------------------------------------------------------------------
/第7章/program/example_ajax_request.py:
--------------------------------------------------------------------------------
1 | import requests
2 |
3 | url_post = 'http://exercise.kingname.info/ajax_1_postbackend'
4 |
5 | html_kingname = requests.post(url_post, json={'name': '青南', 'age': 24}).content.decode()
6 | html_other = requests.post(url_post, json={'name': '无名小卒', 'age': 4}).content.decode()
7 |
8 | print(html_kingname)
9 | print(html_other)
10 |
11 |
12 |
13 | # url = 'http://exercise.kingname.info/ajax_1_backend'
14 | #
15 | # html = requests.get(url).content.decode()
16 | #
17 | # print(html)
18 |
19 |
--------------------------------------------------------------------------------
/第7章/program/example_fake_ajax.py:
--------------------------------------------------------------------------------
1 | import json
2 | import requests
3 | import re
4 |
5 | url = 'http://exercise.kingname.info/exercise_ajax_2.html'
6 | html = requests.get(url).content.decode()
7 |
8 | code_json = re.search("secret = '(.*?)'", html, re.S).group(1)
9 | code_dict = json.loads(code_json)
10 | print(code_dict['code'])
11 |
12 |
13 | # html_json = '{"code": "\u884c\u52a8\u4ee3\u53f7\uff1a\u5929\u738b\u76d6\u5730\u864e"}'
14 | # html_dict = json.loads(html_json)
15 | # print(html_dict)
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/第7章/program/example_headers.py:
--------------------------------------------------------------------------------
1 | import requests
2 | import json
3 |
4 | url = 'http://exercise.kingname.info/exercise_headers_backend'
5 |
6 | headers = {
7 | 'Accept': '*/*',
8 | 'Accept-Encoding': 'gzip, deflate, br',
9 | 'Accept-Language': 'zh-CN,zh;q=0.8,en;q=0.6',
10 | 'anhao': 'kingname',
11 | 'Connection': 'keep-alive',
12 | 'Content-Type': 'application/json; charset=utf-8',
13 | 'DNT': '1',
14 | 'Host': 'exercise.kingname.info',
15 | 'Referer': 'http://exercise.kingname.info/exercise_headers.html',
16 | 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36',
17 | 'X-Requested-With': 'XMLHttpRequest',
18 | }
19 |
20 | html_json = requests.get(url, headers=headers).content.decode()
21 | html_dict = json.loads(html_json)
22 |
23 | print(html_dict)
24 |
25 |
--------------------------------------------------------------------------------
/第7章/program/example_json.py:
--------------------------------------------------------------------------------
1 | import json
2 |
3 | person = {
4 | 'basic_info': {'name': '青南',
5 | 'age': 24,
6 | 'sex': 'male',
7 | 'merry': False},
8 |
9 | 'work_info': {'salary': 99999,
10 | 'position': 'engineer',
11 | 'department': None}
12 | }
13 |
14 | # person_json = json.dumps(person)
15 | person_json_indent = json.dumps(person, indent=4)
16 | print(f'person_json_indent变量的类型为:{type(person_json_indent)}')
17 | person_dict = json.loads(person_json_indent)
18 | print(f'person_dict变量的类型为:{type(person_dict)}')
19 | print(person_dict['basic_info']['name'])
20 |
21 |
22 |
23 | # book_list = [
24 | # {'name': '三国演义',
25 | # 'price': 99.99},
26 | # {'name': '西游记',
27 | # 'price': 100.0},
28 | # {'name': '红楼梦',
29 | # 'price': 10.50},
30 | # {'name': '水浒传',
31 | # 'price': 20.22}
32 | # ]
33 | #
34 | # book_json = json.dumps(book_list, indent=4)
35 | # print(book_json)
--------------------------------------------------------------------------------
/第7章/program/example_login.py:
--------------------------------------------------------------------------------
1 | import requests
2 | import json
3 |
4 | url = 'http://exercise.kingname.info/ajax_4_backend'
5 | code_json = requests.post(url, json={'username': 'kingname', 'password': 'genius'}).content.decode()
6 | code_dict = json.loads(code_json)
7 | print(code_dict['code'])
8 |
--------------------------------------------------------------------------------
/第7章/program/example_multi_requests.py:
--------------------------------------------------------------------------------
1 | import json
2 | import re
3 | import requests
4 |
5 | url = 'http://exercise.kingname.info/exercise_ajax_3.html'
6 | first_ajax_url = 'http://exercise.kingname.info/ajax_3_backend'
7 | second_ajax_url = 'http://exercise.kingname.info/ajax_3_postbackend'
8 |
9 | page_html = requests.get(url).content.decode()
10 | secret_2 = re.search("secret_2 = '(.*?)'", page_html, re.S).group(1)
11 |
12 | ajax_1_json = requests.get(first_ajax_url).content.decode()
13 | ajax_1_dict = json.loads(ajax_1_json)
14 | secret_1 = ajax_1_dict['code']
15 |
16 | ajax_2_json = requests.post(second_ajax_url, json={'name': '青南',
17 | 'age': 24,
18 | 'secret1': secret_1,
19 | 'secret2': secret_2}).content.decode()
20 | ajax_2_dict = json.loads(ajax_2_json)
21 | code = ajax_2_dict['code']
22 | print(f'最终页面显示的内容:{code}')
23 |
24 | # url = 'http://exercise.kingname.info/ajax_3_postbackend'
25 | # return_json_1 = requests.post(url, json={"name": "xx",
26 | # "age": 24,
27 | # "secret1": "123",
28 | # "secret2": "456"})
29 | #
30 | # return_json_2 = requests.post(url, json={"name": "xx",
31 | # "age": 24})
32 | # print(f'乱写secret1和secret2, 返回:{json.loads(return_json_1.content.decode())}')
33 | # print(f'不写secret1和secret2,返回:{json.loads(return_json_2.content.decode())}')
34 |
35 |
36 | # html_json = '{"code": "\u884c\u52a8\u4ee3\u53f7\uff1a\u54ce\u54df\u4e0d\u9519\u54e6", "success": true}'
37 | # html_dict = json.loads(html_json)
38 | #
39 | # print(html_dict)
--------------------------------------------------------------------------------
/第7章/program/example_selenium.py:
--------------------------------------------------------------------------------
1 | from selenium import webdriver
2 | from selenium.webdriver.support.ui import WebDriverWait
3 | from selenium.webdriver.common.by import By
4 | from selenium.webdriver.support import expected_conditions as EC
5 |
6 | driver = webdriver.Chrome('./chromedriver')
7 | driver.get('http://exercise.kingname.info/exercise_advanced_ajax.html')
8 |
9 | try:
10 | WebDriverWait(driver, 30).until(EC.text_to_be_present_in_element((By.CLASS_NAME, "content"), '通关'))
11 | except Exception as _:
12 | print('网页加载太慢,不想等了。')
13 |
14 | element = driver.find_element_by_xpath('//div[@class="content"]')
15 | print(f'异步加载的内容是:{element.text}')
16 |
17 | driver.quit()
18 |
19 | # html = driver.page_source
20 | # print(html)
21 | # input('按任意键结束。')
22 |
23 | # element = driver.find_element_by_id("passwd-id") #如果有多个符合条件的,返回第一个
24 | # element = driver.find_element_by_name("passwd") #如果有多个符合条件的,返回第一个
25 | # element_list = driver.find_elements_by_id("passwd-id") #以列表形式返回所有的符合条件的element
26 | # element_list = driver.find_elements_by_name("passwd") #以列表形式返回所有的符合条件的element
27 | # comment = driver.find_element_by_xpath('//div[@class="content"]')
28 | # print(comment.text)
29 | #
30 | # comment = driver.find_elements_by_xpath('//p[starts-with(@id, "content_")]')
31 | # for each in comment:
32 | # print(each.text)
33 |
34 |
35 |
--------------------------------------------------------------------------------
/第7章/program/homework_letv.py:
--------------------------------------------------------------------------------
1 | import re
2 | import json
3 | import requests
4 |
5 |
6 | class LetvSpider(object):
7 |
8 | COMMENT_URL = 'http://api.my.le.com/vcm/api/list?jsonp=jQuery171029717156927084076_1504105496754&' \
9 | 'cid=2&type=video&rows=20&page=1&sort=&source=1&listType=1&xid={xid}&pid={pid}&' \
10 | 'ctype=cmt%2Cimg%2Cvote'
11 |
12 | HEADERS = {
13 | 'Accept': 'application/json, text/javascript, */*; q=0.01',
14 | 'Accept-Encoding': 'gzip, deflate',
15 | 'Accept-Language': 'zh-CN,zh;q=0.8,en;q=0.6',
16 | 'Connection': 'keep-alive',
17 | 'Cookie': 'http://api.my.le.com/vcm/api/list?jsonp=jQuery171029717156927084076_1504105496754&cid=2&type=video&rows=20&page=1&sort=&source=1&listType=1&xid=30744694&pid=10026177&ctype=cmt%2Cimg%2Cvote',
18 | 'DNT': '1',
19 | 'Host': 'api.my.le.com',
20 | 'Referer': 'http://www.le.com/ptv/vplay/30744694.html?ref=index_focus_1',
21 | 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36',
22 | 'X-Requested-With': 'XMLHttpRequest'
23 | }
24 |
25 | def __init__(self, url):
26 | self.necessary_info = {}
27 | self.url = url
28 | self.get_necessary_id()
29 | self.get_comment()
30 |
31 | def get_source(self, url, headers):
32 | return requests.get(url, headers).content.decode()
33 |
34 | def get_necessary_id(self):
35 | source = self.get_source(self.url, self.HEADERS)
36 | vid = re.search('vid: (\d+)', source).group(1)
37 | pid = re.search('pid: (\d+)', source).group(1)
38 | self.necessary_info['xid'] = vid
39 | self.necessary_info['pid'] = pid
40 |
41 | def get_comment(self):
42 | url = self.COMMENT_URL.format(xid=self.necessary_info['xid'],
43 | pid=self.necessary_info['pid'])
44 | source = self.get_source(url, self.HEADERS)
45 | source_json = source[source.find('{"'): -1]
46 | comment_dict = json.loads(source_json)
47 | comments = comment_dict['data']
48 | for comment in comments:
49 | print(f'发帖人: {comment["user"]["username"]}, 评论内容:{comment["content"]}')
50 | if __name__ == '__main__':
51 | spider = LetvSpider('http://www.le.com/ptv/vplay/30744694.html?ref=index_focus_1')
52 |
53 |
--------------------------------------------------------------------------------
/第8章/program/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/第8章/program/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | $USER_HOME$/.subversion
6 | true
7 |
8 |
--------------------------------------------------------------------------------
/第8章/program/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/第8章/program/.idea/program.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/第8章/program/captcha.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingname/SourceCodeOfBook/ab7275108994dca564905818b678bbd2f771c18e/第8章/program/captcha.png
--------------------------------------------------------------------------------
/第8章/program/example_captcha.py:
--------------------------------------------------------------------------------
1 | import requests
2 | import lxml.html
3 |
4 | url = 'http://exercise.kingname.info/exercise_captcha.html'
5 | url_check = 'http://exercise.kingname.info/exercise_captcha_check'
6 |
7 | session = requests.Session()
8 | html = session.get(url).content
9 | selector = lxml.html.fromstring(html)
10 | captcha_url = selector.xpath('//img/@src')[0]
11 |
12 | #下载验证码文件
13 | image = requests.get('http://exercise.kingname.info/' + captcha_url).content
14 | with open('captcha.png', 'wb') as f:
15 | f.write(image)
16 |
17 | captcha = input('请查看captcha.png文件,然后在输入到这里:')
18 | after_check = session.post(url_check, data={'captcha': captcha})
19 |
20 | print(f'输入验证码以后,网站返回:{after_check.content.decode()}')
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/第8章/program/example_captchakiller.py:
--------------------------------------------------------------------------------
1 | """
2 | CaptchaKiller.py
3 | ~~~~~~~~~~~~~~~~
4 | This module is used to query captcha by Yundama which is an anti-captcha website.
5 | The work flow is:
6 |
7 | upload captcha-img to the website by its api and then query the result.
8 | """
9 | import requests
10 | import json
11 | import time
12 |
13 |
14 | class CaptchaKiller(object):
15 | captcha_username = 'kingname'
16 | captcha_password = 'baiguanlab'
17 | captcha_appid = 1
18 | captcha_appkey = '22cc5376925e9387a23cf797cb9ba745'
19 | captcha_codetype = '1004'
20 | captcha_url = 'http://api.yundama.com/api.php?method=upload'
21 | captcha_result_url = 'http://api.yundama.com/api.php?cid={}&method=result'
22 |
23 | def __init__(self):
24 | pass
25 |
26 | def get_captcha_by_cid(self, cid, timeout=15):
27 | while timeout > 0:
28 | response = requests.get(self.captcha_result_url.format(cid)).text
29 | response_dict = json.loads(response)
30 | print(response_dict, '——还剩:{}秒...'.format(timeout))
31 | captcha = response_dict['text']
32 | if response_dict['text']:
33 | return captcha
34 | time.sleep(1)
35 | timeout -= 1
36 | return ''
37 |
38 | def query_captcha(self, filename):
39 | data = {'method': 'upload',
40 | 'username': self.captcha_username,
41 | 'password': self.captcha_password,
42 | 'appid': self.captcha_appid,
43 | 'appkey': self.captcha_appkey,
44 | 'codetype': self.captcha_codetype,
45 | 'timeout': '60'}
46 |
47 | f = open(filename, 'rb')
48 | file = {'file': f}
49 | response = requests.post(self.captcha_url, data, files=file).text
50 | f.close()
51 | response_dict = json.loads(response)
52 | if 'cid' not in response_dict:
53 | print('请在官网上查询此错误码: {}'.format(response_dict['ret']))
54 | exit()
55 | captcha = response_dict['text']
56 | if not captcha:
57 | cid = response_dict['cid']
58 | captcha = self.get_captcha_by_cid(cid)
59 |
60 | return captcha
61 |
62 | if __name__ == '__main__':
63 | captcha_killer = CaptchaKiller()
64 | captcha = captcha_killer.query_captcha('captcha.png')
65 | print(captcha)
66 |
67 |
--------------------------------------------------------------------------------
/第8章/program/example_login.py:
--------------------------------------------------------------------------------
1 | import requests
2 |
3 | login_url = 'http://exercise.kingname.info/exercise_login'
4 | login_success_url = 'http://exercise.kingname.info/exercise_login_success'
5 |
6 | data = {'username': 'kingname',
7 | 'password': 'genius',
8 | 'remember': 'Yes'}
9 |
10 | session = requests.Session()
11 | before_login = session.get('http://exercise.kingname.info/exercise_login_success').text
12 | print(before_login)
13 | print('==============开始登录==============')
14 | session.post(login_url, data=data).text
15 | after_login = session.get('http://exercise.kingname.info/exercise_login_success').text
16 | print(after_login)
17 |
--------------------------------------------------------------------------------
/第8章/program/example_login_zhihu.py:
--------------------------------------------------------------------------------
1 | from selenium import webdriver
2 | from selenium.webdriver.common.keys import Keys
3 | import time
4 |
5 | driver = webdriver.Chrome('./chromedriver') #填写你的chromedriver的路径
6 | driver.get("https://www.zhihu.com/#signin")
7 |
8 | elem = driver.find_element_by_name("account") #寻找账号输入框
9 | elem.clear()
10 | elem.send_keys("xxx@gmail.com") #输入账号
11 | password = driver.find_element_by_name('password') #寻找密码输入框
12 | password.clear()
13 | password.send_keys("12345678") #输入密码
14 | input('请在网页上点击倒立的文字,完成以后回到这里按任意键继续。')
15 | elem.send_keys(Keys.RETURN) #模拟键盘回车键
16 | time.sleep(10) #这里可以直接sleep, 也可以使用上一章讲到的等待某个条件出现
17 | print(driver.page_source)
18 | driver.quit()
19 |
--------------------------------------------------------------------------------
/第8章/program/example_tesseract.py:
--------------------------------------------------------------------------------
1 | import pytesseract
2 | from PIL import Image
3 | image = Image.open('验证码.png')
4 | code = pytesseract.image_to_string(image)
5 | print(code)
6 |
7 |
--------------------------------------------------------------------------------
/第8章/program/example_zhihu.com.py:
--------------------------------------------------------------------------------
1 | import requests
2 |
3 | headers = {
4 | 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
5 | 'Accept-Encoding': 'gzip, deflate, br',
6 | 'Accept-Language': 'zh-CN,zh;q=0.8,en;q=0.6',
7 | 'Cache-Control': 'max-age=0',
8 | 'Connection': 'keep-alive',
9 | 'Cookie': 'd_c0="AECABUmzUQqPTt7Waah4-eNbKDE-ndMJmbU=|1470061508"; _za=76e3ff3b-0533-43bf-b323-54658bd219d8; _zap=fa08520e-f1ca-4f87-a421-b6b59c6cf594; _ga=GA1.2.135017213.1483973195; aliyungf_tc=AQAAAMVqMBUkrAEA660zPUq1ZRkS1BkJ; acw_tc=AQAAAIpAiy5gpgIA660zPaCio8BzRvqC; q_c1=cbe92683816948f98932e79afd8cdc4b|1498356032000|1476533291000; q_c1=cbe92683816948f98932e79afd8cdc4b|1498356032000|1476533291000; r_cap_id="YmM1MmFmNGM1MmNlNGNkMTg5MjI5NzhiYzYyY2VkNzE=|1498699312|98d42498b4ba65f9e781f6630a3c28736e65836c"; cap_id="ZGVjYjcyYTI0YTQyNDlhODllYTM2YTZiMDE4ZTgzN2I=|1498699312|c3dea0bceadbc1df3eb778e3ec6b53b1d21e09e4"; z_c0=Mi4wQUFEQWpMb2VBQUFBUUlBRlNiTlJDaGNBQUFCaEFsVk5PT043V1FBOUVkTXdlY3lZc2tfb2YwWC11ZnlxQTM0eVFR|1498699320|a361ff4798ea0ffe11182a897b5940718fd8d823; s-q=%E5%86%B7%E7%9F%A5%E8%AF%86; s-i=3; sid=i5hj46q8; __utma=51854390.1170851395.1497785466.1500334504.1500338344.23; __utmc=51854390; __utmz=51854390.1500338344.23.12.utmcsr=zhihu.com|utmccn=(referral)|utmcmd=referral|utmcct=/; __utmv=51854390.100-1|2=registration_date=20131006=1^3=entry_date=20131006=1; _xsrf=73fd62f055ae3b91964cd5f9ea35fb03',
10 | 'DNT': '1',
11 | 'Host': 'www.zhihu.com',
12 | 'Upgrade-Insecure-Requests': '1',
13 | 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36'
14 | }
15 | session = requests.Session()
16 | source = session.get('https://zhihu.com', headers=headers, verify=False).content.decode()
17 | x = session.get('https://www.zhihu.com/inbox/3117566900', headers=headers, verify=False).content.decode()
18 | print(x)
19 |
--------------------------------------------------------------------------------
/第8章/program/guokr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingname/SourceCodeOfBook/ab7275108994dca564905818b678bbd2f771c18e/第8章/program/guokr.png
--------------------------------------------------------------------------------
/第8章/program/homework_guokr.py:
--------------------------------------------------------------------------------
1 | import requests
2 | import time
3 | import json
4 | import re
5 |
6 |
7 | login_url = 'https://account.guokr.com/sign_in/'
8 | email = 'greensouth@foxmail.com'
9 | password = 'xxxxxxxxx'
10 |
11 | captcha_username = 'kingname'
12 | captcha_password = 'baiguanlab'
13 | captcha_appid = 1
14 | captcha_appkey = '22cc5376925e9387a23cf797cb9ba745'
15 | captcha_codetype = '1004'
16 | captcha_url = 'http://api.yundama.com/api.php?method=upload'
17 | captcha_result_url = 'http://api.yundama.com/api.php?cid={}&method=result'
18 | guokr_captcha_url = 'https://account.guokr.com/captcha/{}'
19 |
20 | data_guokr = {
21 | 'username': email,
22 | 'password': password,
23 | 'permanent': 'y'
24 | }
25 | header = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36'}
26 |
27 |
28 | def get_captcha_by_cid(cid, timeout=15):
29 | while timeout > 0:
30 | response = requests.get(captcha_result_url.format(cid)).text
31 | response_dict = json.loads(response)
32 | print(response_dict, '——还剩:{}秒...'.format(timeout))
33 | captcha = response_dict['text']
34 | if response_dict['text']:
35 | return captcha
36 | time.sleep(1)
37 | timeout -= 1
38 | return ''
39 |
40 |
41 | def query_captcha(filename):
42 | data = {'method': 'upload', 'username': captcha_username, 'password': captcha_password, 'appid': captcha_appid,
43 | 'appkey': captcha_appkey, 'codetype': captcha_codetype, 'timeout': '60'}
44 | f = open(filename, 'rb')
45 | file = {'file': f}
46 | response = requests.post(captcha_url, data, files=file).text
47 | f.close()
48 | response_dict = json.loads(response)
49 | if 'cid' not in response_dict:
50 | print('请在官网上查询此错误码: {}'.format(response_dict['ret']))
51 | exit()
52 | captcha = response_dict['text']
53 | if not captcha:
54 | cid = response_dict['cid']
55 | captcha = get_captcha_by_cid(cid)
56 |
57 | return captcha
58 |
59 |
60 | session = requests.Session()
61 |
62 | #直接访问登录页面,先获取必要信息:csrf_token, captcha_rand 和captcha地址
63 | html = session.get(login_url, headers=header).content.decode()
64 |
65 | csrf_token = re.findall('name="csrf_token" type="hidden" value="(.*?)">', html, re.S)
66 | if not csrf_token:
67 | print('不能获取csrf_token, 无法登录。')
68 | exit()
69 | csrf_token = csrf_token[0]
70 |
71 | captcha_rand = re.findall('id="captchaRand" value="(.*?)">', html, re.S)
72 | if not captcha_rand:
73 | print('不能获取captcha_rand, 无法登录。')
74 | exit()
75 |
76 | captcha_rand = captcha_rand[0]
77 | with open('guokr.png', 'wb') as f:
78 | f.write(requests.get(guokr_captcha_url.format(captcha_rand)).content)
79 |
80 | captcha_solution = query_captcha('guokr.png')
81 | data_guokr['captcha'] = captcha_solution
82 | data_guokr['captcha_rand'] = captcha_rand
83 | data_guokr['csrf_token'] = csrf_token
84 | result = session.post(login_url, data=data_guokr, headers=header).content
85 |
86 | #在登录成功以后,访问其他页面只需要使用session.get即可。
87 | profile = session.get('http://www.guokr.com/settings/profile/', headers=header).content
88 | print(profile.decode())
89 |
90 |
91 |
--------------------------------------------------------------------------------
/第8章/program/验证码.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingname/SourceCodeOfBook/ab7275108994dca564905818b678bbd2f771c18e/第8章/program/验证码.png
--------------------------------------------------------------------------------
/第9章/program/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/第9章/program/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | $USER_HOME$/.subversion
6 | true
7 |
8 |
--------------------------------------------------------------------------------
/第9章/program/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/第9章/program/.idea/program.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/第9章/program/extract.py:
--------------------------------------------------------------------------------
1 | import re
2 | import sys
3 | import redis
4 |
5 | client = redis.StrictRedis()
6 | for line in sys.stdin:
7 | cookie = re.search('>>>(.*?)<<<', line)
8 | if cookie:
9 | print(f'拿到Cookies:{cookie.group(1)}')
10 | client.lpush('login_cookies', cookie.group(1))
11 |
--------------------------------------------------------------------------------
/第9章/program/generate_token.py:
--------------------------------------------------------------------------------
1 | from selenium import webdriver
2 | import time
3 |
4 | service_args = ["--proxy=127.0.0.1:8080", '--ignore-ssl-errors=yes']
5 |
6 |
7 | def run():
8 | print('start to Token')
9 | driver = webdriver.PhantomJS(service_args=service_args)
10 | driver.get('http://xxxx')
11 | time.sleep(5)
12 | driver.close()
13 |
14 | if __name__ == '__main__':
15 | run()
--------------------------------------------------------------------------------
/第9章/program/homework_keep.py:
--------------------------------------------------------------------------------
1 | import requests
2 | import json
3 |
4 | headers = {
5 | 'Host': 'api.gotokeep.com',
6 | 'Connection': 'Keep-Alive',
7 | 'Accept-Encoding': 'gzip',
8 | 'x-os-version': '7.1.2',
9 | 'x-channel': 'yingyongbao',
10 | 'x-locale': 'zh--CN',
11 | 'x-screen-height': '640',
12 | 'x-is-new-device': 'false',
13 | 'User-Agent': 'Keep+4.7.0%2FAndroid+7.1.2-8558+Xiaomi+Redmi+4X',
14 | 'x-manufacturer': 'Xiaomi',
15 | 'x-keep-timezone': 'Asia/Shanghai',
16 | 'x-screen-width': '360',
17 | 'x-os': 'Android',
18 | 'x-device-id': '86469803711655111111111111111111086dfde7',
19 | 'x-version-name': '4.7.0',
20 | 'x-user-id': '59952648094afb2485a122e3',
21 | 'x-version-code': '8558',
22 | 'x-model': 'Redmi+4X',
23 | 'Authorization': 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJfaWQiOiI1OTk1MjY0ODA5NGFmYjI0ODVhMTIyZTMiLCJ1c2VybmFtZSI6IuavlOWwlOS8iuaBqSIsImF2YXRhciI6IiIsImdlbmRlciI6Ik0iLCJkZXZpY2VJZCI6Ijg2NDY5ODAzNzExNjU1MTExMTExMTExMTExMTExMTExMDg2ZGZkZTciLCJpc3MiOiJodHRwOi8vd3d3LmdvdG9rZWVwLmNvbS8iLCJleHAiOjE1MTI3NDgzODIsImlhdCI6MTUwNDEwODM4Mn0.I_xG1LI2twDQuoPf1S_N2Ywdv-DqugGLGt5Toz-O7RU'
24 | }
25 |
26 | url = 'https://api.gotokeep.com/social/v3/timeline/hot'
27 | hot = requests.get(url, headers=headers).content.decode()
28 | hot_dict = json.loads(hot)
29 | datas = hot_dict['data']['entries']
30 | for data in datas:
31 | print(f'用户: {data["author"]["username"]},内容:{data["content"]}')
32 |
--------------------------------------------------------------------------------
/第9章/program/juejin.py:
--------------------------------------------------------------------------------
1 | import requests
2 | import json
3 |
4 |
5 | url = 'https://timeline-merger-ms.juejin.im/v1/get_entry_by_timeline?before=&category=57be7c18128fe1005fa902de&limit=20&src=ios&type='
6 |
7 | headers = {
8 | 'Host': 'timeline-merger-ms.juejin.im',
9 | 'Accept': '*/*',
10 | 'Cookie': 'QINGCLOUDELB=47f7a729e0fcb7fdf0b3143c89790b65ab7e48fb3972913efd95d72fe838c4fb|W0Nyw|W0Nyw',
11 | 'User-Agent': 'Xitu/5.3.0 (iPad; iOS 11.4; Scale/2.00)',
12 | 'Accept-Language': 'zh-Hans-CN;q=1',
13 | 'Accept-Encoding': 'br, gzip, deflate',
14 | 'Connection': 'keep-alive'}
15 |
16 | result = requests.get(url, headers=headers, verify=False).text
17 | article_info = json.loads(result)
18 | for article in article_info['d']['entrylist']:
19 | print(article['title'])
20 |
--------------------------------------------------------------------------------
/第9章/program/parse_one_site.py:
--------------------------------------------------------------------------------
1 | # def response(flow):
2 | # req = flow.request
3 | # response = flow.response
4 | # if 'kingname.info' in req.url:
5 | # print('这是kingname的网站,也是我的目标网站')
6 | # print(f'请求的headers是: {req.headers}')
7 | # print(f'请求的UA是: {req.headers["User-Agent"]}')
8 | # print(f'返回的内容是:{response.text}')
9 |
10 |
11 | def response(flow):
12 | req = flow.request
13 | if 'kingname.info' in req.url:
14 | cookies = req.headers.get('Cookie', '')
15 | if cookies:
16 | print(f'>>>{cookies}<<<')
17 |
--------------------------------------------------------------------------------
/第9章/program/parse_request.py:
--------------------------------------------------------------------------------
1 | def request(flow):
2 | req = flow.request
3 | print(f'当前请求的URL为: {req.url}')
4 | print(f'当前的请求方式为: {req.method}')
5 | print(f'当前的Cookies为: {req.cookies}')
6 | print(f'请求的body为: {req.text}')
--------------------------------------------------------------------------------
/第9章/program/parse_response.py:
--------------------------------------------------------------------------------
1 | import json
2 | def response(flow):
3 | resp = flow.response
4 | print(f'返回的头部为:{resp.headers}')
5 | print(f'返回的body为:{json.loads(resp.content)}')
6 |
--------------------------------------------------------------------------------