diff --git a/2018/08/10/A01-hello-world/index.html b/2018/08/10/A01-hello-world/index.html index df38181f068d07ba1927ad0b7067d46c1e7f6425..4876dc4a6b1525c151e11e74525065f2aaafd82d 100644 --- a/2018/08/10/A01-hello-world/index.html +++ b/2018/08/10/A01-hello-world/index.html @@ -110,7 +110,7 @@ - + @@ -733,7 +733,7 @@
-  Hexo  Github Pages +  Github Pages  Hexo
diff --git a/2018/08/15/A02-hexo-blog/index.html b/2018/08/15/A02-hexo-blog/index.html index 361d4b7c7a63cfd3a2381391e46100878fd0d263..1983ac21c6558c12779834e977fb6855c84aa32a 100644 --- a/2018/08/15/A02-hexo-blog/index.html +++ b/2018/08/15/A02-hexo-blog/index.html @@ -110,7 +110,7 @@ - + @@ -747,7 +747,7 @@ -
 

Hexo

 

Github Pages

+
 

Github Pages

 

Hexo

diff --git a/2018/08/25/A03-markdown/index.html b/2018/08/25/A03-markdown/index.html index c39f4dd812a321accc5374d7771855ef790be6fd..8b51473ad9f38821bbcf27a2252aa2e1c146544b 100644 --- a/2018/08/25/A03-markdown/index.html +++ b/2018/08/25/A03-markdown/index.html @@ -110,7 +110,7 @@ - + @@ -736,7 +736,7 @@
-  主题个性化  Hexo  Material X  spfk +  Hexo  主题个性化  Material X  spfk
@@ -756,7 +756,7 @@
-  Hexo  Github Pages +  Github Pages  Hexo
diff --git a/2018/08/27/A04-Hexo-blog-topic-personalization/index.html b/2018/08/27/A04-Hexo-blog-topic-personalization/index.html index b558e890c08e3aa5460db7c8e3b3514cb22d4448..532fe8fcd09b5ef14e7dd2c964323cdb9e923b0b 100644 --- a/2018/08/27/A04-Hexo-blog-topic-personalization/index.html +++ b/2018/08/27/A04-Hexo-blog-topic-personalization/index.html @@ -110,7 +110,7 @@ - + @@ -916,7 +916,7 @@ -
 

主题个性化

 

Hexo

 

Material X

 

spfk

+
 

Hexo

 

主题个性化

 

Material X

 

spfk

diff --git a/2018/08/29/A05-markdown-editor/index.html b/2018/08/29/A05-markdown-editor/index.html index 1e5d3cf5364d70e6d398bc8f99fbbefd40ce3586..613be97ed0586165b4de99f2a492d36cdab799ea 100644 --- a/2018/08/29/A05-markdown-editor/index.html +++ b/2018/08/29/A05-markdown-editor/index.html @@ -110,7 +110,7 @@ - + @@ -817,7 +817,7 @@
-  主题个性化  Hexo  Material X  spfk +  Hexo  主题个性化  Material X  spfk
diff --git a/2018/09/09/A06-install-ubuntu18.04/index.html b/2018/09/09/A06-install-ubuntu18.04/index.html index 9c2bc81dffe87d1b1a826f89164d411ae1e62ebc..d20536218795da50fcf855e1129dd16e14e9ed0f 100644 --- a/2018/09/09/A06-install-ubuntu18.04/index.html +++ b/2018/09/09/A06-install-ubuntu18.04/index.html @@ -110,7 +110,7 @@ - + diff --git a/2018/09/13/A07-Python3-basic-C01/index.html b/2018/09/13/A07-Python3-basic-C01/index.html index c0d9dc2ec20aaa426a3849b156d0c4b6e1c51759..7d4f31a68c8cd5d25087f60d7b0974ff29a90514 100644 --- a/2018/09/13/A07-Python3-basic-C01/index.html +++ b/2018/09/13/A07-Python3-basic-C01/index.html @@ -110,7 +110,7 @@ - + diff --git a/2018/09/16/A08-Python3-basic-C02/index.html b/2018/09/16/A08-Python3-basic-C02/index.html index e18c146e85bbffcf07fa693beee79831822daef9..b1f5315ca272104acfb37555273396e41fad09b7 100644 --- a/2018/09/16/A08-Python3-basic-C02/index.html +++ b/2018/09/16/A08-Python3-basic-C02/index.html @@ -110,7 +110,7 @@ - + diff --git a/2018/10/11/A09-Python3-basic-C03/index.html b/2018/10/11/A09-Python3-basic-C03/index.html index 4189c1bfc162aa9f8c0dacfa8b6826a7a3a42d1c..06e559c8d701f01e3881bd5d6a7ccdb45986aaa4 100644 --- a/2018/10/11/A09-Python3-basic-C03/index.html +++ b/2018/10/11/A09-Python3-basic-C03/index.html @@ -110,7 +110,7 @@ - + diff --git a/2018/10/24/A10-Python3-basic-C04/index.html b/2018/10/24/A10-Python3-basic-C04/index.html index 2f16129c1eb0f3693f6cbd6cb82e30dedf3fb8d6..be16edfb3106f1c817a40ae8025e11c7973f025e 100644 --- a/2018/10/24/A10-Python3-basic-C04/index.html +++ b/2018/10/24/A10-Python3-basic-C04/index.html @@ -110,7 +110,7 @@ - + diff --git a/2018/10/27/A11-Python3-basic-C05/index.html b/2018/10/27/A11-Python3-basic-C05/index.html index 7b3224b736931519885b03b1aedeef36020c3bd2..491d1dbaf997810d9ad4db27721c18a1e05e38b9 100644 --- a/2018/10/27/A11-Python3-basic-C05/index.html +++ b/2018/10/27/A11-Python3-basic-C05/index.html @@ -110,7 +110,7 @@ - + diff --git a/2018/10/30/A12-Python3-basic-C06/index.html b/2018/10/30/A12-Python3-basic-C06/index.html index 5d082201cbe9ff37de2c32e3ee2391e1583c3444..1780cee6acd3e3a8c10fc7bc7c9ba2dfe423836e 100644 --- a/2018/10/30/A12-Python3-basic-C06/index.html +++ b/2018/10/30/A12-Python3-basic-C06/index.html @@ -110,7 +110,7 @@ - + diff --git a/2018/11/03/A13-Python3-basic-C07/index.html b/2018/11/03/A13-Python3-basic-C07/index.html index c4f4407ac1f9e4922254345e3d36d0fd108c99c3..aa47f7d9b6888eadafd51e3c75c8e04df950aed7 100644 --- a/2018/11/03/A13-Python3-basic-C07/index.html +++ b/2018/11/03/A13-Python3-basic-C07/index.html @@ -110,7 +110,7 @@ - + diff --git a/2018/11/11/A14-Python3-basic-C08/index.html b/2018/11/11/A14-Python3-basic-C08/index.html index 7c6a4469ec6c0cff7cc832b580b0cbe2eaae4913..1c1c770dcd7b0f3cc28a8acf4cf903099ad0ae61 100644 --- a/2018/11/11/A14-Python3-basic-C08/index.html +++ b/2018/11/11/A14-Python3-basic-C08/index.html @@ -110,7 +110,7 @@ - + diff --git a/2018/11/16/A15-Python3-basic-C09/index.html b/2018/11/16/A15-Python3-basic-C09/index.html index 854b8161edae988637b53c6dd938e7c29fd814c8..bb3d2f825ec96966ea7640ddcb48f4c23c1bbc14 100644 --- a/2018/11/16/A15-Python3-basic-C09/index.html +++ b/2018/11/16/A15-Python3-basic-C09/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/01/18/A16-deploy-two-or-more-hexo-blogs/index.html b/2019/01/18/A16-deploy-two-or-more-hexo-blogs/index.html index 91036b596c803b141756441c2f777e86d098e7d2..75ecf0ada9c3facf8ac3013e529f6232a35b310f 100644 --- a/2019/01/18/A16-deploy-two-or-more-hexo-blogs/index.html +++ b/2019/01/18/A16-deploy-two-or-more-hexo-blogs/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/02/05/A17-happy-new-year/index.html b/2019/02/05/A17-happy-new-year/index.html index f82a2049c3fd2260010487c727267ec7bdd62ab6..3cd9dbc42e97cba54496ca7d145ed868e9c5dd72 100644 --- a/2019/02/05/A17-happy-new-year/index.html +++ b/2019/02/05/A17-happy-new-year/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/02/10/A18-free-cdn/index.html b/2019/02/10/A18-free-cdn/index.html index 77914c7ea620ab29dd0d30bb2cca21d241b14c10..7421b271eacfdae23d0b80045f9c5538ca594c53 100644 --- a/2019/02/10/A18-free-cdn/index.html +++ b/2019/02/10/A18-free-cdn/index.html @@ -110,7 +110,7 @@ - + @@ -748,7 +748,7 @@
-  Pygame  Python +  Python  Pygame
diff --git a/2019/03/10/A19-install-pygame/index.html b/2019/03/10/A19-install-pygame/index.html index acc6c4dba362cb853c6400f3492a6af4efa456f2..6952025e94cc893fb1f4bf97c9cfda0a374ee555 100644 --- a/2019/03/10/A19-install-pygame/index.html +++ b/2019/03/10/A19-install-pygame/index.html @@ -110,7 +110,7 @@ - + @@ -657,7 +657,7 @@ -
 

Pygame

 

Python

+
 

Python

 

Pygame

diff --git a/2019/04/14/A20-install-deepin15.9/index.html b/2019/04/14/A20-install-deepin15.9/index.html index 62e572908681eccf341db665fb86be40fb31c77f..e3e94bbafaac9a3c48b3a188eb80f74ec7dbdd14 100644 --- a/2019/04/14/A20-install-deepin15.9/index.html +++ b/2019/04/14/A20-install-deepin15.9/index.html @@ -110,7 +110,7 @@ - + @@ -748,7 +748,7 @@
-  Pygame  Python +  Python  Pygame
diff --git a/2019/04/15/A21-PEP8/index.html b/2019/04/15/A21-PEP8/index.html index cc86e4f0998760cbb44c8442aea5f504a2924a82..13b7d4d79c67eeaae7f92efa37d3a77da05f2494 100644 --- a/2019/04/15/A21-PEP8/index.html +++ b/2019/04/15/A21-PEP8/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/05/14/A22-eclipse-connects-to-sql/index.html b/2019/05/14/A22-eclipse-connects-to-sql/index.html index c1dd224b6cf978a1072013e20582fd88f7dfe584..c55430b973ac46dc6b122181be7c46331b632d34 100644 --- a/2019/05/14/A22-eclipse-connects-to-sql/index.html +++ b/2019/05/14/A22-eclipse-connects-to-sql/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/07/31/A26-hexo-add-https/index.html b/2019/07/31/A26-hexo-add-https/index.html index 07a75dcc4d4d121ba8e4df75ea53aaac3105079c..494bfded5e964b91c0fa08c4d5a5984245aa64a5 100644 --- a/2019/07/31/A26-hexo-add-https/index.html +++ b/2019/07/31/A26-hexo-add-https/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/08/01/A27-image-hosting/index.html b/2019/08/01/A27-image-hosting/index.html index e1f6ca40f4f2253ae25f12d12c163e78a38225fd..5fda62f02d8f19fd9f82d6b3e3a4e4cb7e206f13 100644 --- a/2019/08/01/A27-image-hosting/index.html +++ b/2019/08/01/A27-image-hosting/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/08/11/A28-hexo-add-https/index.html b/2019/08/11/A28-hexo-add-https/index.html index debbd8aac3d2c8c3e052e57dc889a1ab08a24722..e035144507e30400159eb04267c6db4904304608 100644 --- a/2019/08/11/A28-hexo-add-https/index.html +++ b/2019/08/11/A28-hexo-add-https/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/08/23/A23-beian/index.html b/2019/08/23/A23-beian/index.html index 23af1d880013e0d6f4482dfc71507ba7c977ced1..33472a204ec42fa1ef05730a278794742a47de0d 100644 --- a/2019/08/23/A23-beian/index.html +++ b/2019/08/23/A23-beian/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/08/23/A24-instant.page/index.html b/2019/08/23/A24-instant.page/index.html index 7e9bd008d9cf0990b6e0c191b0f19a2f929e9f5c..00e8de97ff0817f29e687f7a255f5832b56120d6 100644 --- a/2019/08/23/A24-instant.page/index.html +++ b/2019/08/23/A24-instant.page/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/08/23/A25-SB/index.html b/2019/08/23/A25-SB/index.html index 6c3d65d635ee550b57ecabea8e24699511e2afa5..db35089ee958e02dcf5e127b2f3ec185bd4fce92 100644 --- a/2019/08/23/A25-SB/index.html +++ b/2019/08/23/A25-SB/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/08/23/A29-Python3-spider-C01/index.html b/2019/08/23/A29-Python3-spider-C01/index.html index 53c12221abe755e144db9c828622b8e356448e0e..18b64c18dce2655ff77ac827aed29064ace61a70 100644 --- a/2019/08/23/A29-Python3-spider-C01/index.html +++ b/2019/08/23/A29-Python3-spider-C01/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/08/23/A30-Python3-spider-C02/index.html b/2019/08/23/A30-Python3-spider-C02/index.html index 1d4667e5bb4a55d87ebf5bae6f8332e956fe4deb..651241b8bbce5c1dd1a5e0adde22a6331fb891c9 100644 --- a/2019/08/23/A30-Python3-spider-C02/index.html +++ b/2019/08/23/A30-Python3-spider-C02/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/08/23/A31-Python3-spider-C03/index.html b/2019/08/23/A31-Python3-spider-C03/index.html index 2c2726e4bcf2b35e798b8fa3109b87a3a208062c..9a0daaa2e5a531b02445093da341883df751f431 100644 --- a/2019/08/23/A31-Python3-spider-C03/index.html +++ b/2019/08/23/A31-Python3-spider-C03/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/08/23/A32-Python3-spider-C04/index.html b/2019/08/23/A32-Python3-spider-C04/index.html index 0979b42e3cae8677163ddfcb5dd8a7f56a85e6d2..3d8f3404ceb39bb6bc5d48c98257a4cc4b317d2c 100644 --- a/2019/08/23/A32-Python3-spider-C04/index.html +++ b/2019/08/23/A32-Python3-spider-C04/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/08/23/A33-selenium/index.html b/2019/08/23/A33-selenium/index.html index d89e913e38e44fbbb8828472a7fd35dc73231c3d..bf5305834e408eef976166041e578931f45f672d 100644 --- a/2019/08/23/A33-selenium/index.html +++ b/2019/08/23/A33-selenium/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/08/23/A34-UserAgent/index.html b/2019/08/23/A34-UserAgent/index.html index 5d819e027c8684743b3fb2acdad6487285988bd6..03078c8014a1af23f9ea2af3dcc1a137c8c8ecec 100644 --- a/2019/08/23/A34-UserAgent/index.html +++ b/2019/08/23/A34-UserAgent/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/08/23/A35-Python3-spider-C05/index.html b/2019/08/23/A35-Python3-spider-C05/index.html index 25a3d221120e3a6865ee0f9dabd6ca59a21ea640..accb9bd0c3aad3d286aed6a4eeb34fb002d64f01 100644 --- a/2019/08/23/A35-Python3-spider-C05/index.html +++ b/2019/08/23/A35-Python3-spider-C05/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/08/24/A36-Python3-spider-C06/index.html b/2019/08/24/A36-Python3-spider-C06/index.html index 8c4adcf7b68abbc7f81b42cf55a32cb8216626f2..4ce6a59a79d9f6f3d3e6f2df37c016a22189b371 100644 --- a/2019/08/24/A36-Python3-spider-C06/index.html +++ b/2019/08/24/A36-Python3-spider-C06/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/08/25/A37-Python3-spider-C07/index.html b/2019/08/25/A37-Python3-spider-C07/index.html index a12538d4ac00f400609061e99a573ec6c0104c8c..a745f0cd98bd4932f40fa6f676c6a95358d10bbc 100644 --- a/2019/08/25/A37-Python3-spider-C07/index.html +++ b/2019/08/25/A37-Python3-spider-C07/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/08/26/A38-Python3-spider-C08/index.html b/2019/08/26/A38-Python3-spider-C08/index.html index 52c4b43218f5c0bd7a9c612824852032e7f4eb28..201eb29223929876f6cde4c2f5ab4632059249cf 100644 --- a/2019/08/26/A38-Python3-spider-C08/index.html +++ b/2019/08/26/A38-Python3-spider-C08/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/08/27/A39-Python3-spider-C09/index.html b/2019/08/27/A39-Python3-spider-C09/index.html index dba3a267d6bbaf9e88685ae272b3eacd273a3fb4..bdedfd60fc4744829bf7e5e38330ab9871e706fd 100644 --- a/2019/08/27/A39-Python3-spider-C09/index.html +++ b/2019/08/27/A39-Python3-spider-C09/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/09/03/A40-Python3-spider-C10/index.html b/2019/09/03/A40-Python3-spider-C10/index.html index f8e6257df6994de08ec3f419c174f3afe4bf64a4..e071d2a8c3759489d5e21704a4b02493836a6959 100644 --- a/2019/09/03/A40-Python3-spider-C10/index.html +++ b/2019/09/03/A40-Python3-spider-C10/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/09/04/A41-Python3-spider-C11/index.html b/2019/09/04/A41-Python3-spider-C11/index.html index 68edd0aa12e5cb10948348e7b7d387cfb5342b53..9a637a497cea48a9c288b92366950b30d2357c58 100644 --- a/2019/09/04/A41-Python3-spider-C11/index.html +++ b/2019/09/04/A41-Python3-spider-C11/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/09/05/A42-Python3-spider-C12/index.html b/2019/09/05/A42-Python3-spider-C12/index.html index 8a519db5f59ecde60b3c664e1fffa31782471c99..c29a60179c87f3673607530b226f435b502b414e 100644 --- a/2019/09/05/A42-Python3-spider-C12/index.html +++ b/2019/09/05/A42-Python3-spider-C12/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/09/07/A43-Python3-spider-C13/index.html b/2019/09/07/A43-Python3-spider-C13/index.html index 7d79966c740b40baacd3dc5601e429cf0f9cb062..6bd489c338a17848ac4e11138b0d9e49375845d7 100644 --- a/2019/09/07/A43-Python3-spider-C13/index.html +++ b/2019/09/07/A43-Python3-spider-C13/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/09/08/A44-Python3-spider-C14/index.html b/2019/09/08/A44-Python3-spider-C14/index.html index 8175d078f74be5f64dc16ea15e2a5dcad41a502d..491e7070787a576760409cfef8bcf2fa99d1f574 100644 --- a/2019/09/08/A44-Python3-spider-C14/index.html +++ b/2019/09/08/A44-Python3-spider-C14/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/09/10/A45-Python3-spider-C15/index.html b/2019/09/10/A45-Python3-spider-C15/index.html index 27315cb3e61a9ea8a165cd22df0239d5bdacc3f2..1e3e615d7e4506d102d461e17bcef8553f9d9a51 100644 --- a/2019/09/10/A45-Python3-spider-C15/index.html +++ b/2019/09/10/A45-Python3-spider-C15/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/09/14/A46-Python3-spider-C16/index.html b/2019/09/14/A46-Python3-spider-C16/index.html index 869876bfa75bf90eb2f65b1e1b612c4004f63c4b..d2ee66c851db7827815a33d25be330cac680e6a9 100644 --- a/2019/09/14/A46-Python3-spider-C16/index.html +++ b/2019/09/14/A46-Python3-spider-C16/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/09/16/A47-hexo-deployed-to-github-and-coding/index.html b/2019/09/16/A47-hexo-deployed-to-github-and-coding/index.html index 8da78a0b7e5f24104faa1bb72cfa39b3ea964750..f33def29b856bb01ed2f7ce8a3637e40452db3cc 100644 --- a/2019/09/16/A47-hexo-deployed-to-github-and-coding/index.html +++ b/2019/09/16/A47-hexo-deployed-to-github-and-coding/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/09/17/A48-submit-search-engine-inclusion/index.html b/2019/09/17/A48-submit-search-engine-inclusion/index.html index 2e0d21f102352d8fbdd8d780b6f774356e5e7229..4474a9632cc8157276b2da2f0e44cb50e361318e 100644 --- a/2019/09/17/A48-submit-search-engine-inclusion/index.html +++ b/2019/09/17/A48-submit-search-engine-inclusion/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/09/18/A49-Python3-spider-C17/index.html b/2019/09/18/A49-Python3-spider-C17/index.html index 46bcb8b50fa9f5a5bae71895f4c9091a1a25dad0..d6fd760187b451074109fcd9e56f0f019e192757 100644 --- a/2019/09/18/A49-Python3-spider-C17/index.html +++ b/2019/09/18/A49-Python3-spider-C17/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/09/21/A50-Python3-spider-C18/index.html b/2019/09/21/A50-Python3-spider-C18/index.html index 854123a34251da3bfa4b1fcaf6f2d17debdce4e0..1e62bafddb0751498d6af28e50e500af2bd0cc75 100644 --- a/2019/09/21/A50-Python3-spider-C18/index.html +++ b/2019/09/21/A50-Python3-spider-C18/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/09/24/A51-pyspider-maoyantop100/index.html b/2019/09/24/A51-pyspider-maoyantop100/index.html index 356d5b45e57db51c37f413b24a487716b9ce8f22..c66969acf9f1a0e718d1e6737eb130d9f37dd750 100644 --- a/2019/09/24/A51-pyspider-maoyantop100/index.html +++ b/2019/09/24/A51-pyspider-maoyantop100/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/09/28/A52-pyspider-doubantop250/index.html b/2019/09/28/A52-pyspider-doubantop250/index.html index 96cb0dba6dc7f35d775c4d182cd352b023d0ff9e..14bc341120ecd7c0e71d06576189f9de122f872e 100644 --- a/2019/09/28/A52-pyspider-doubantop250/index.html +++ b/2019/09/28/A52-pyspider-doubantop250/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/09/29/A53-hexo-backup/index.html b/2019/09/29/A53-hexo-backup/index.html index 5fdc578dca41d832f639a2fb3e34c56ca90534d2..41d546012dbffdd70dce09a47676e681bd1436c4 100644 --- a/2019/09/29/A53-hexo-backup/index.html +++ b/2019/09/29/A53-hexo-backup/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/10/09/A54-pyspider-anjuke/index.html b/2019/10/09/A54-pyspider-anjuke/index.html index 18611f28b4f6a6ce38ebcd92a488f757ee168bbc..4eb4ccc67a215054e9022c84b8331f3b1257967d 100644 --- a/2019/10/09/A54-pyspider-anjuke/index.html +++ b/2019/10/09/A54-pyspider-anjuke/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/10/12/A55-pyspider-hupu/index.html b/2019/10/12/A55-pyspider-hupu/index.html index 7a3c25328b3553b8e14d5dd95b285546082cad65..c8ae3aa500f2eadd352c8c7cfde3815772043b78 100644 --- a/2019/10/12/A55-pyspider-hupu/index.html +++ b/2019/10/12/A55-pyspider-hupu/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/10/21/A56-pyspider-bilibili-login/index.html b/2019/10/21/A56-pyspider-bilibili-login/index.html index f37e32992751d4e1a537411c8805e8a4056d8340..a2996a106a8f7b03cab6064d1f29b2d622ab5be8 100644 --- a/2019/10/21/A56-pyspider-bilibili-login/index.html +++ b/2019/10/21/A56-pyspider-bilibili-login/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/10/21/A57-pyspider-12306-login/index.html b/2019/10/21/A57-pyspider-12306-login/index.html index fe6ad4999d92894c03c90dbcc0cbcbbbfaad87f9..c824db474d02ba880d0e042cddababc9af04fb65 100644 --- a/2019/10/21/A57-pyspider-12306-login/index.html +++ b/2019/10/21/A57-pyspider-12306-login/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/10/21/A58-pyspider-58tongcheng/index.html b/2019/10/21/A58-pyspider-58tongcheng/index.html index ae3bbf6d19ad4ffea10a23c8acf91f72072ad3d8..c3721af905ade7100f911a46d511ff52e1fc68be 100644 --- a/2019/10/21/A58-pyspider-58tongcheng/index.html +++ b/2019/10/21/A58-pyspider-58tongcheng/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/11/15/A59-pyspider-guazi/index.html b/2019/11/15/A59-pyspider-guazi/index.html index 9658abac95b357f87277ab64802842472fd680b9..9798e311c25883415041f6dbf5a0c16e47a02d58 100644 --- a/2019/11/15/A59-pyspider-guazi/index.html +++ b/2019/11/15/A59-pyspider-guazi/index.html @@ -110,7 +110,7 @@ - + diff --git a/2019/12/31/A60-2019-summary/index.html b/2019/12/31/A60-2019-summary/index.html index 9d91875182bd8fe45aa9534ea2556b9bef5c8687..31f19a57de1486e7fb1f2cde28891bdf522d8a2b 100644 --- a/2019/12/31/A60-2019-summary/index.html +++ b/2019/12/31/A60-2019-summary/index.html @@ -110,7 +110,7 @@ - + diff --git a/2020/01/10/A61-build-a-SSR-server-with-VPS/index.html b/2020/01/10/A61-build-a-SSR-server-with-VPS/index.html index d99fd93e4ed7eaaf45169174b5dcb24c556caf58..c0d4ebfba764096b76b28c7e0a63ca15cd7e2b53 100644 --- a/2020/01/10/A61-build-a-SSR-server-with-VPS/index.html +++ b/2020/01/10/A61-build-a-SSR-server-with-VPS/index.html @@ -110,7 +110,7 @@ - + diff --git a/2020/03/20/A62-NumPy-01/index.html b/2020/03/20/A62-NumPy-01/index.html index 291b4737b42a12173dd40809a0b04ed582b382b0..c52d9f478d382064e3db42649dec8c1187c240f3 100644 --- a/2020/03/20/A62-NumPy-01/index.html +++ b/2020/03/20/A62-NumPy-01/index.html @@ -110,7 +110,7 @@ - + diff --git a/2020/03/22/A63-NumPy-02/index.html b/2020/03/22/A63-NumPy-02/index.html index 63d323925ee1300b5489e083a8cfc1a0df1eb9e8..c122e15a4882885620bea7d1ca39bb116bbddd86 100644 --- a/2020/03/22/A63-NumPy-02/index.html +++ b/2020/03/22/A63-NumPy-02/index.html @@ -110,7 +110,7 @@ - + diff --git a/2020/03/24/A64-NumPy-03/index.html b/2020/03/24/A64-NumPy-03/index.html index 81ddb83dfbe4deae69800d30c02d2546d0062489..2c7d61446f423ac34215a5667fd60ac4c3882cf2 100644 --- a/2020/03/24/A64-NumPy-03/index.html +++ b/2020/03/24/A64-NumPy-03/index.html @@ -110,7 +110,7 @@ - + diff --git a/2020/03/26/A65-NumPy-04/index.html b/2020/03/26/A65-NumPy-04/index.html index dfa6d05fbd553017a65080d3c89542d49e11bdff..26d52e6198224443c1aaf4b5ac8c7c4c4aa499af 100644 --- a/2020/03/26/A65-NumPy-04/index.html +++ b/2020/03/26/A65-NumPy-04/index.html @@ -110,7 +110,7 @@ - + diff --git a/2020/03/28/A66-NumPy-05/index.html b/2020/03/28/A66-NumPy-05/index.html index 72906a2cc0514f916bd1854955fcc7a214c3b3c4..3a5992ccb6507b98aab44d70df455f5bfb88afa1 100644 --- a/2020/03/28/A66-NumPy-05/index.html +++ b/2020/03/28/A66-NumPy-05/index.html @@ -110,7 +110,7 @@ - + diff --git a/2020/03/30/A67-NumPy-06/index.html b/2020/03/30/A67-NumPy-06/index.html index a6922593b1ae40f1f2ae952cfc9fdd5c502d4922..a6ce3bf694dcf57ff99cff2d60a0b66177ce33bc 100644 --- a/2020/03/30/A67-NumPy-06/index.html +++ b/2020/03/30/A67-NumPy-06/index.html @@ -110,7 +110,7 @@ - + diff --git a/2020/04/10/A68-Matplotlib-01/index.html b/2020/04/10/A68-Matplotlib-01/index.html index 2549a04dc28a46472e297a9b8b857d350022b291..4bcdeb5021b23aa95d4fd4e4d68b7d9e76fd4f1f 100644 --- a/2020/04/10/A68-Matplotlib-01/index.html +++ b/2020/04/10/A68-Matplotlib-01/index.html @@ -110,7 +110,7 @@ - + diff --git a/2020/04/12/A69-Matplotlib-02/index.html b/2020/04/12/A69-Matplotlib-02/index.html index b272adb8c24c1a431071a38f782fac90412944a2..2035090c0034fe8d0273d3aae7885a46d3854b20 100644 --- a/2020/04/12/A69-Matplotlib-02/index.html +++ b/2020/04/12/A69-Matplotlib-02/index.html @@ -110,7 +110,7 @@ - + diff --git a/2020/04/14/A70-Matplotlib-03/index.html b/2020/04/14/A70-Matplotlib-03/index.html index 89b8c08caf314867a0bbf711e9c4066f96527d5e..986d424f1e06cd1698adf0a3d4cd790a67f1630d 100644 --- a/2020/04/14/A70-Matplotlib-03/index.html +++ b/2020/04/14/A70-Matplotlib-03/index.html @@ -110,7 +110,7 @@ - + diff --git a/2020/04/16/A71-Matplotlib-04/index.html b/2020/04/16/A71-Matplotlib-04/index.html index 95395740335163492f30895536e5f6215492567e..44d4e8c181484e0fe100d945b3f90123bdedd92c 100644 --- a/2020/04/16/A71-Matplotlib-04/index.html +++ b/2020/04/16/A71-Matplotlib-04/index.html @@ -110,7 +110,7 @@ - + diff --git a/2020/04/18/A72-Matplotlib-05/index.html b/2020/04/18/A72-Matplotlib-05/index.html index 7e7a7f793a0eafdb5ac89b31c4cda26f8ca7959d..2d944bf9a75bfd6c87d95b39cc21f5b7b47470e7 100644 --- a/2020/04/18/A72-Matplotlib-05/index.html +++ b/2020/04/18/A72-Matplotlib-05/index.html @@ -110,7 +110,7 @@ - + diff --git a/2020/04/21/A73-Matplotlib-06/index.html b/2020/04/21/A73-Matplotlib-06/index.html index 2f1e84b1647b19b118b301dec2cdc25c1365a97d..f8fd3271166e49ae0cb8899b56f08ecd27cf0ef4 100644 --- a/2020/04/21/A73-Matplotlib-06/index.html +++ b/2020/04/21/A73-Matplotlib-06/index.html @@ -110,7 +110,7 @@ - + diff --git a/2020/04/24/A74-Matplotlib-07/index.html b/2020/04/24/A74-Matplotlib-07/index.html index 724224d85d0a111eb6224b23384ed2145ce495a7..f5a9970195f365a7371da23d0b01f7417e579c23 100644 --- a/2020/04/24/A74-Matplotlib-07/index.html +++ b/2020/04/24/A74-Matplotlib-07/index.html @@ -110,7 +110,7 @@ - + diff --git a/2020/04/30/A75-Matplotlib-08/index.html b/2020/04/30/A75-Matplotlib-08/index.html index 5029e9a7264b9fa258f0e2b60a7c0f8e922063b7..7098002faf9a2df1cf4f6af3ff9d6a8f11b02c2c 100644 --- a/2020/04/30/A75-Matplotlib-08/index.html +++ b/2020/04/30/A75-Matplotlib-08/index.html @@ -110,7 +110,7 @@ - + diff --git a/2020/06/03/A76-Matplotlib-09/index.html b/2020/06/03/A76-Matplotlib-09/index.html index 3f2dace7bab7692cda2890b38bba97269e847790..7a7ac4fe590107c9c9e4b22045b3fb935f341957 100644 --- a/2020/06/03/A76-Matplotlib-09/index.html +++ b/2020/06/03/A76-Matplotlib-09/index.html @@ -110,7 +110,7 @@ - + diff --git a/2020/06/08/A77-Matplotlib-10/index.html b/2020/06/08/A77-Matplotlib-10/index.html index 574029d88e12be612f7075297e3770fe5fb368fa..8b68761c23522fc6461477372873ab93a28c8eec 100644 --- a/2020/06/08/A77-Matplotlib-10/index.html +++ b/2020/06/08/A77-Matplotlib-10/index.html @@ -110,7 +110,7 @@ - + diff --git a/2020/06/09/A78-Matplotlib-11/index.html b/2020/06/09/A78-Matplotlib-11/index.html index c2bec590d43505a17cb9a29ab3485000e230bf9a..278a7a5260dba88d260b55e9b26f62ffc6d16379 100644 --- a/2020/06/09/A78-Matplotlib-11/index.html +++ b/2020/06/09/A78-Matplotlib-11/index.html @@ -110,7 +110,7 @@ - + diff --git a/2020/06/11/A79-Pandas-01/index.html b/2020/06/11/A79-Pandas-01/index.html index fcc8701086f978253206adc62103eb26568fc7b1..77fac6d34f7bb0e5da541d8a93b56aaa7373b4c3 100644 --- a/2020/06/11/A79-Pandas-01/index.html +++ b/2020/06/11/A79-Pandas-01/index.html @@ -110,7 +110,7 @@ - + diff --git a/2020/06/13/A80-Pandas-02/index.html b/2020/06/13/A80-Pandas-02/index.html index 74cfebd3fc4f94d0e2b499d42937e614f396474f..6019268e2f2ea12968a5aaeae444178cd0843568 100644 --- a/2020/06/13/A80-Pandas-02/index.html +++ b/2020/06/13/A80-Pandas-02/index.html @@ -110,7 +110,7 @@ - + diff --git a/2020/06/14/A81-Pandas-03/index.html b/2020/06/14/A81-Pandas-03/index.html index 484ad6bccf7189bde662cb356e9454a5d40eceaa..14da2ebceb9348810e6a3249f1f23aa33ad28f55 100644 --- a/2020/06/14/A81-Pandas-03/index.html +++ b/2020/06/14/A81-Pandas-03/index.html @@ -110,7 +110,7 @@ - + diff --git a/2020/06/15/A82-Pandas-04/index.html b/2020/06/15/A82-Pandas-04/index.html index 210d56c018a9ed8eafd04d76c0deb4cd22d6292d..305dc2a88d49997f33c569ba06685e45fc167086 100644 --- a/2020/06/15/A82-Pandas-04/index.html +++ b/2020/06/15/A82-Pandas-04/index.html @@ -110,7 +110,7 @@ - + diff --git a/2020/06/16/A83-Pandas-05/index.html b/2020/06/16/A83-Pandas-05/index.html index 760c6b80c0b5086ddf1a6b7205aeb7e2a6d0a288..7a07658aca71943dd4d8f919cb1ffe30cd1e46a8 100644 --- a/2020/06/16/A83-Pandas-05/index.html +++ b/2020/06/16/A83-Pandas-05/index.html @@ -110,7 +110,7 @@ - + diff --git a/2020/06/17/A84-Pandas-06/index.html b/2020/06/17/A84-Pandas-06/index.html index 552f93eb0efce9a2a4fd92b2d01aa41b43aed7aa..3a9c3e669cd4f04408ad905568e608685f00d46e 100644 --- a/2020/06/17/A84-Pandas-06/index.html +++ b/2020/06/17/A84-Pandas-06/index.html @@ -110,7 +110,7 @@ - + diff --git a/2020/06/21/A85-Pandas-07/index.html b/2020/06/21/A85-Pandas-07/index.html index 49e30df0168793465eabc101383fc299ab7f97f4..ab15ed51dcc33cba7c97145851356a370afec69e 100644 --- a/2020/06/21/A85-Pandas-07/index.html +++ b/2020/06/21/A85-Pandas-07/index.html @@ -110,7 +110,7 @@ - + diff --git a/2020/06/22/A86-Pandas-08/index.html b/2020/06/22/A86-Pandas-08/index.html index 37acb0bdf3834f21aff2129c1bc0529178555ad3..361ebbcde7497767b329442736616a2d17471337 100644 --- a/2020/06/22/A86-Pandas-08/index.html +++ b/2020/06/22/A86-Pandas-08/index.html @@ -110,7 +110,7 @@ - + diff --git a/2020/06/25/A87-Pandas-09/index.html b/2020/06/25/A87-Pandas-09/index.html index 53b7d816e32df28ef5016a2d00cfc7d6596b7702..b6e4937c289a2aa1ca71dbec2a2dbaf629fd00d9 100644 --- a/2020/06/25/A87-Pandas-09/index.html +++ b/2020/06/25/A87-Pandas-09/index.html @@ -110,7 +110,7 @@ - + diff --git a/2020/06/26/A88-Pandas-10/index.html b/2020/06/26/A88-Pandas-10/index.html index 3c834bcb8227876af8fd2b0a78fc117d0163cc47..89ff737ab2d9fd11666dceba85a414b66ecbb45d 100644 --- a/2020/06/26/A88-Pandas-10/index.html +++ b/2020/06/26/A88-Pandas-10/index.html @@ -110,7 +110,7 @@ - + diff --git a/2020/07/06/A89-COVID-19/index.html b/2020/07/06/A89-COVID-19/index.html index 81d2ea2a087bb6b1c6eb2eda8a202dfd11fa165b..3e1b4129796cb2933772f43dc59614cd481a9800 100644 --- a/2020/07/06/A89-COVID-19/index.html +++ b/2020/07/06/A89-COVID-19/index.html @@ -110,7 +110,7 @@ - + diff --git a/2020/07/13/A90-pyspider-51job/index.html b/2020/07/13/A90-pyspider-51job/index.html index 1ce6c486eecd03e7a53f33e939b71e99ce6aa0ff..a8688399d0576e1ac61f7d520d5490174f399e49 100644 --- a/2020/07/13/A90-pyspider-51job/index.html +++ b/2020/07/13/A90-pyspider-51job/index.html @@ -110,7 +110,7 @@ - + diff --git a/archives/2018/08/index.html b/archives/2018/08/index.html index 2f286953f5c2f1656dbf8b119a1fa950aa5a69b5..5fa3f79ef69cdca5e233a1ae2c3ed9bf5bb35c6e 100644 --- a/archives/2018/08/index.html +++ b/archives/2018/08/index.html @@ -110,7 +110,7 @@ - + @@ -682,10 +682,10 @@
-  主题个性化 -  Hexo +  主题个性化 +  Material X  spfk @@ -961,10 +961,10 @@
-  Hexo -  Github Pages +  Hexo +
diff --git a/archives/2018/09/index.html b/archives/2018/09/index.html index dd61c573ae6cb2dd7079b0cf7df4e279e8ee2baa..d0f7b32c46b216014b2fe0021112e4be994db2ee 100644 --- a/archives/2018/09/index.html +++ b/archives/2018/09/index.html @@ -110,7 +110,7 @@ - + diff --git a/archives/2018/10/index.html b/archives/2018/10/index.html index 999868163eddf63e9af18b1ba138f8567f467df5..95a1fdc6ec08114a72dfc489690d86b7e4f5b072 100644 --- a/archives/2018/10/index.html +++ b/archives/2018/10/index.html @@ -110,7 +110,7 @@ - + diff --git a/archives/2018/11/index.html b/archives/2018/11/index.html index 960c954e7bc75d4fd730afdc7faa93ae3fc325b6..8325467c6e421e213a1e4552d2c6e5ec25521cea 100644 --- a/archives/2018/11/index.html +++ b/archives/2018/11/index.html @@ -110,7 +110,7 @@ - + diff --git a/archives/2018/index.html b/archives/2018/index.html index 683a46d1c0ba68614f306285c326763ae0d9fa25..17bc269d2780c174265244827b5d88b007f7287e 100644 --- a/archives/2018/index.html +++ b/archives/2018/index.html @@ -110,7 +110,7 @@ - + diff --git a/archives/2018/page/2/index.html b/archives/2018/page/2/index.html index 7fac4d21c25c8463ffcf8bea0d993cc5f66cb366..fdc41af89772f097370e69b483bfb3a6cad7fea3 100644 --- a/archives/2018/page/2/index.html +++ b/archives/2018/page/2/index.html @@ -110,7 +110,7 @@ - + @@ -668,10 +668,10 @@
-  主题个性化 -  Hexo +  主题个性化 +  Material X  spfk @@ -947,10 +947,10 @@ diff --git a/archives/2019/01/index.html b/archives/2019/01/index.html index 7b755ab515a8190b536396d066327570e8540dec..4560bf57ee59c5d1a546bca588c89c45da85f2b3 100644 --- a/archives/2019/01/index.html +++ b/archives/2019/01/index.html @@ -110,7 +110,7 @@ - + diff --git a/archives/2019/02/index.html b/archives/2019/02/index.html index f8798fe9baf0bc9dd80bcefb2d03acc4014cc1bc..83bdd17c03e92347ad208a28ed5dacd9ab9e76d1 100644 --- a/archives/2019/02/index.html +++ b/archives/2019/02/index.html @@ -110,7 +110,7 @@ - + diff --git a/archives/2019/03/index.html b/archives/2019/03/index.html index 62af15abb150c74a5b8508d6ec035e9b3d3718a3..42854ee6c74c5fe2db4cce3630790e473c4767ca 100644 --- a/archives/2019/03/index.html +++ b/archives/2019/03/index.html @@ -110,7 +110,7 @@ - + @@ -545,10 +545,10 @@ diff --git a/archives/2019/04/index.html b/archives/2019/04/index.html index c8f88acb23ab1f11b436aa9b241a90d8b228a9c9..2d592e79b59f47f2a91c34ef92a72ddb5cbc3b8c 100644 --- a/archives/2019/04/index.html +++ b/archives/2019/04/index.html @@ -110,7 +110,7 @@ - + diff --git a/archives/2019/05/index.html b/archives/2019/05/index.html index 0abf69079b9f06c59982ca4eb65b865ddb660e5f..a5c40b9904bb60fd1b9230f7a977d8683e2e3f22 100644 --- a/archives/2019/05/index.html +++ b/archives/2019/05/index.html @@ -110,7 +110,7 @@ - + diff --git a/archives/2019/07/index.html b/archives/2019/07/index.html index 424e65433d2a207d12c62dbdde68f6a97ddaad38..b6d3a274783c5323e9538d73cc46674a75320c4d 100644 --- a/archives/2019/07/index.html +++ b/archives/2019/07/index.html @@ -110,7 +110,7 @@ - + diff --git a/archives/2019/08/index.html b/archives/2019/08/index.html index ecb4b247f5289ffd70ef98c6f7ab1062719f0a6f..d1c65f24da9ebf459dd1c4354571217cf3fa9fff 100644 --- a/archives/2019/08/index.html +++ b/archives/2019/08/index.html @@ -110,7 +110,7 @@ - + diff --git a/archives/2019/08/page/2/index.html b/archives/2019/08/page/2/index.html index cb34137fd15cac7ccf289190079aa56a488ca5fa..6918599cda4690f411ec2880cbb8ad8390b81e31 100644 --- a/archives/2019/08/page/2/index.html +++ b/archives/2019/08/page/2/index.html @@ -110,7 +110,7 @@ - + diff --git a/archives/2019/09/index.html b/archives/2019/09/index.html index 4583cf8a4fe133aedee9ae9e3527b106f024745b..a2a2b89e9d2238dea218bc4c612a5d9f807bc977 100644 --- a/archives/2019/09/index.html +++ b/archives/2019/09/index.html @@ -110,7 +110,7 @@ - + diff --git a/archives/2019/09/page/2/index.html b/archives/2019/09/page/2/index.html index d05f89072b061883faf4a4a39a40b631fee3d354..2e0fb8b41614e4b88969721e3972c2e39cc378cd 100644 --- a/archives/2019/09/page/2/index.html +++ b/archives/2019/09/page/2/index.html @@ -110,7 +110,7 @@ - + diff --git a/archives/2019/10/index.html b/archives/2019/10/index.html index 2ef1c5c3e8a466bc08c6e244276f44a95da3ca1c..266a09c20b46e62fe56b35d43d45150d546f6e47 100644 --- a/archives/2019/10/index.html +++ b/archives/2019/10/index.html @@ -110,7 +110,7 @@ - + diff --git a/archives/2019/11/index.html b/archives/2019/11/index.html index c22604e7f4b1c3583679b3db235f56a3bdb83f82..d7d90657ddb7492fcd5019102a86815607a3d52d 100644 --- a/archives/2019/11/index.html +++ b/archives/2019/11/index.html @@ -110,7 +110,7 @@ - + diff --git a/archives/2019/12/index.html b/archives/2019/12/index.html index caf70fc7fc3b723cdf116698c8e0a9060e4919e0..5f66e7b4ff6b1a1859fd76ff1d2f322735179ef2 100644 --- a/archives/2019/12/index.html +++ b/archives/2019/12/index.html @@ -110,7 +110,7 @@ - + diff --git a/archives/2019/index.html b/archives/2019/index.html index 4c94944b6708ebb4b8035da5302c02d9cb6a2d45..e1f5c9ab45b5904c8374b58725fcd5d64b5ab586 100644 --- a/archives/2019/index.html +++ b/archives/2019/index.html @@ -110,7 +110,7 @@ - + diff --git a/archives/2019/page/2/index.html b/archives/2019/page/2/index.html index 53ddff8e9600dbcc888fb32be3051346b408c788..15305a6781541b6e787bb4e9ac1b73c391e2358f 100644 --- a/archives/2019/page/2/index.html +++ b/archives/2019/page/2/index.html @@ -110,7 +110,7 @@ - + diff --git a/archives/2019/page/3/index.html b/archives/2019/page/3/index.html index a89809b5d2653a6df72dd4333fa8ed658252b079..fe86b2933a570fd6bc95d77323adf82c94beac26 100644 --- a/archives/2019/page/3/index.html +++ b/archives/2019/page/3/index.html @@ -110,7 +110,7 @@ - + diff --git a/archives/2019/page/4/index.html b/archives/2019/page/4/index.html index 0e7cd9613d0ab960ff97c3d2b6a609e15e8fe33c..681a2c71360638599403ee8d193da8b541d640de 100644 --- a/archives/2019/page/4/index.html +++ b/archives/2019/page/4/index.html @@ -110,7 +110,7 @@ - + diff --git a/archives/2019/page/5/index.html b/archives/2019/page/5/index.html index eed22df07b3fdaa38b9cf7ae0fd436b5d2444880..b755ac679074b17318c1f87b49f7de218d770eb9 100644 --- a/archives/2019/page/5/index.html +++ b/archives/2019/page/5/index.html @@ -110,7 +110,7 @@ - + @@ -668,10 +668,10 @@ diff --git a/archives/2020/01/index.html b/archives/2020/01/index.html index 326df5008d80aae6931a75f058e9cb2939f62396..b12baf6c52940d7dcecdc77a896161cc44cc2175 100644 --- a/archives/2020/01/index.html +++ b/archives/2020/01/index.html @@ -110,7 +110,7 @@ - + diff --git a/archives/2020/03/index.html b/archives/2020/03/index.html index 1955f2d290f7a71c37283bade7d4a198aaa1c9e8..c690eb3e7f11143f5c2508d905beeea24f5ddad9 100644 --- a/archives/2020/03/index.html +++ b/archives/2020/03/index.html @@ -110,7 +110,7 @@ - + diff --git a/archives/2020/04/index.html b/archives/2020/04/index.html index 13e31e6f6706c53f24a7b50d7ce04d11b08bfa3f..fe16ac4a4f19994778cd1bc07dea3cd433881e7d 100644 --- a/archives/2020/04/index.html +++ b/archives/2020/04/index.html @@ -110,7 +110,7 @@ - + diff --git a/archives/2020/06/index.html b/archives/2020/06/index.html index e6eb4785ee6c6e6fc715a3b0e7b9b3b534e5dec6..29a9fb529b83db774e9337e652b4cfaf0a37c301 100644 --- a/archives/2020/06/index.html +++ b/archives/2020/06/index.html @@ -110,7 +110,7 @@ - + diff --git a/archives/2020/06/page/2/index.html b/archives/2020/06/page/2/index.html index d7b2e9ed8871b7613014a1c61d1be23b16c490bf..332877c5d5e6eebf11f440ef85e947212797824d 100644 --- a/archives/2020/06/page/2/index.html +++ b/archives/2020/06/page/2/index.html @@ -110,7 +110,7 @@ - + diff --git a/archives/2020/07/index.html b/archives/2020/07/index.html index 371f6aaece562cc93aa62a8f47b3d6a34da34f13..27d857425427b837a9872017d60e8ef4c1f8ce78 100644 --- a/archives/2020/07/index.html +++ b/archives/2020/07/index.html @@ -110,7 +110,7 @@ - + diff --git a/archives/2020/index.html b/archives/2020/index.html index 77ac89265f9b57e9032e3cc12dff10bdbc6d4d8a..a1fe959b8b5c02825a168164beb4642572632d53 100644 --- a/archives/2020/index.html +++ b/archives/2020/index.html @@ -110,7 +110,7 @@ - + diff --git a/archives/2020/page/2/index.html b/archives/2020/page/2/index.html index 85dcffbfbd984b103cefafa520756d2a94058b24..6a8e3bd0ef6f69f5aa5aa833f5a9f8bf3dd49f0f 100644 --- a/archives/2020/page/2/index.html +++ b/archives/2020/page/2/index.html @@ -110,7 +110,7 @@ - + diff --git a/archives/2020/page/3/index.html b/archives/2020/page/3/index.html index 8c8431ad1e8d82f1482e4aa9c9fdd1e2f7f005c0..2fd6bb6a460ff99151d860cdc5ab5dd11e4cc62b 100644 --- a/archives/2020/page/3/index.html +++ b/archives/2020/page/3/index.html @@ -110,7 +110,7 @@ - + diff --git a/archives/index.html b/archives/index.html index ee5f204ca48b460c15075e212866ff862cb6a189..057f23ef559bb46f57a2670d15f235bafad2a7e2 100644 --- a/archives/index.html +++ b/archives/index.html @@ -110,7 +110,7 @@ - + diff --git a/archives/page/2/index.html b/archives/page/2/index.html index ee5f204ca48b460c15075e212866ff862cb6a189..057f23ef559bb46f57a2670d15f235bafad2a7e2 100644 --- a/archives/page/2/index.html +++ b/archives/page/2/index.html @@ -110,7 +110,7 @@ - + diff --git a/archives/page/3/index.html b/archives/page/3/index.html index ee5f204ca48b460c15075e212866ff862cb6a189..057f23ef559bb46f57a2670d15f235bafad2a7e2 100644 --- a/archives/page/3/index.html +++ b/archives/page/3/index.html @@ -110,7 +110,7 @@ - + diff --git a/archives/page/4/index.html b/archives/page/4/index.html index ee5f204ca48b460c15075e212866ff862cb6a189..057f23ef559bb46f57a2670d15f235bafad2a7e2 100644 --- a/archives/page/4/index.html +++ b/archives/page/4/index.html @@ -110,7 +110,7 @@ - + diff --git a/archives/page/5/index.html b/archives/page/5/index.html index ee5f204ca48b460c15075e212866ff862cb6a189..057f23ef559bb46f57a2670d15f235bafad2a7e2 100644 --- a/archives/page/5/index.html +++ b/archives/page/5/index.html @@ -110,7 +110,7 @@ - + diff --git a/archives/page/6/index.html b/archives/page/6/index.html index ee5f204ca48b460c15075e212866ff862cb6a189..057f23ef559bb46f57a2670d15f235bafad2a7e2 100644 --- a/archives/page/6/index.html +++ b/archives/page/6/index.html @@ -110,7 +110,7 @@ - + diff --git a/archives/page/7/index.html b/archives/page/7/index.html index ee5f204ca48b460c15075e212866ff862cb6a189..057f23ef559bb46f57a2670d15f235bafad2a7e2 100644 --- a/archives/page/7/index.html +++ b/archives/page/7/index.html @@ -110,7 +110,7 @@ - + diff --git a/archives/page/8/index.html b/archives/page/8/index.html index ee5f204ca48b460c15075e212866ff862cb6a189..057f23ef559bb46f57a2670d15f235bafad2a7e2 100644 --- a/archives/page/8/index.html +++ b/archives/page/8/index.html @@ -110,7 +110,7 @@ - + diff --git a/archives/page/9/index.html b/archives/page/9/index.html index ee5f204ca48b460c15075e212866ff862cb6a189..057f23ef559bb46f57a2670d15f235bafad2a7e2 100644 --- a/archives/page/9/index.html +++ b/archives/page/9/index.html @@ -110,7 +110,7 @@ - + diff --git a/categories/BLOG/index.html b/categories/BLOG/index.html index 6f8eb964ef380b6e17849884f81abef1b53df97f..bce7ec820e9898669a4d547c2cee47521a902ad6 100644 --- a/categories/BLOG/index.html +++ b/categories/BLOG/index.html @@ -110,7 +110,7 @@ - + diff --git a/categories/CDN/index.html b/categories/CDN/index.html index cfc966d372da1781a7bec08eba635128f0173b7d..5b16be636788a26efc81a21124a9712f64e1ae92 100644 --- a/categories/CDN/index.html +++ b/categories/CDN/index.html @@ -110,7 +110,7 @@ - + diff --git a/categories/Hexo/index.html b/categories/Hexo/index.html index 89710160b7fbad493ade0d4bba1d3209670c9b68..c3f8cca94bb395ae61d3795b4e581a41885e5bfc 100644 --- a/categories/Hexo/index.html +++ b/categories/Hexo/index.html @@ -110,7 +110,7 @@ - + @@ -1473,10 +1473,10 @@
-  主题个性化 -  Hexo +  主题个性化 +  Material X  spfk @@ -1623,10 +1623,10 @@ diff --git a/categories/Java/index.html b/categories/Java/index.html index 90d8e44220500bfe7c61a0776f47417f6781949e..fd66cab27f89f9b2a749f04fa1fa891b6ff70221 100644 --- a/categories/Java/index.html +++ b/categories/Java/index.html @@ -110,7 +110,7 @@ - + diff --git a/categories/Linux/index.html b/categories/Linux/index.html index 3dbe0df53fa6249f0fa1b967920aa3a27012b464..0f88b25d8e280b9b9a243cb8dfc15e6ddd0d2da0 100644 --- a/categories/Linux/index.html +++ b/categories/Linux/index.html @@ -110,7 +110,7 @@ - + diff --git a/categories/Markdown/index.html b/categories/Markdown/index.html index 2d1fba0222678da795b7e64e64492b4e16e88e8d..a3bf21763896e49d494d9a4f42c4a0d38a27a4fc 100644 --- a/categories/Markdown/index.html +++ b/categories/Markdown/index.html @@ -110,7 +110,7 @@ - + diff --git "a/categories/Python-\346\225\260\346\215\256\345\210\206\346\236\220/Matplotlib/index.html" "b/categories/Python-\346\225\260\346\215\256\345\210\206\346\236\220/Matplotlib/index.html" index 891a3dfdc358d4e1d3162ef2a5a9c0289928c89e..0ba9dc8dd68eeba59e5e067431ce30c2553ce013 100644 --- "a/categories/Python-\346\225\260\346\215\256\345\210\206\346\236\220/Matplotlib/index.html" +++ "b/categories/Python-\346\225\260\346\215\256\345\210\206\346\236\220/Matplotlib/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/categories/Python-\346\225\260\346\215\256\345\210\206\346\236\220/Matplotlib/page/2/index.html" "b/categories/Python-\346\225\260\346\215\256\345\210\206\346\236\220/Matplotlib/page/2/index.html" index bf0e94231966dbef58688992f4c994fbda486e67..5d8d8ca4e36a9fb03f0b809dcb15180895217241 100644 --- "a/categories/Python-\346\225\260\346\215\256\345\210\206\346\236\220/Matplotlib/page/2/index.html" +++ "b/categories/Python-\346\225\260\346\215\256\345\210\206\346\236\220/Matplotlib/page/2/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/categories/Python-\346\225\260\346\215\256\345\210\206\346\236\220/NumPy/index.html" "b/categories/Python-\346\225\260\346\215\256\345\210\206\346\236\220/NumPy/index.html" index bea1604636322a5985c6ee8ed8b2c8989791ef66..f04cc35f7007920fc03c9f5ba7110b4e033d0225 100644 --- "a/categories/Python-\346\225\260\346\215\256\345\210\206\346\236\220/NumPy/index.html" +++ "b/categories/Python-\346\225\260\346\215\256\345\210\206\346\236\220/NumPy/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/categories/Python-\346\225\260\346\215\256\345\210\206\346\236\220/Pandas/index.html" "b/categories/Python-\346\225\260\346\215\256\345\210\206\346\236\220/Pandas/index.html" index 5129ee590a12ed6f4277e172f256c76d8837b026..893343059427b0294d2118c8b2a4e4e430d65695 100644 --- "a/categories/Python-\346\225\260\346\215\256\345\210\206\346\236\220/Pandas/index.html" +++ "b/categories/Python-\346\225\260\346\215\256\345\210\206\346\236\220/Pandas/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/categories/Python-\346\225\260\346\215\256\345\210\206\346\236\220/index.html" "b/categories/Python-\346\225\260\346\215\256\345\210\206\346\236\220/index.html" index 138d212a8a6278ba47e3e5a7ff8b61d2c4c248e9..14a5fe16bc493cdcfb910f919f81d5581b0ab795 100644 --- "a/categories/Python-\346\225\260\346\215\256\345\210\206\346\236\220/index.html" +++ "b/categories/Python-\346\225\260\346\215\256\345\210\206\346\236\220/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/categories/Python-\346\225\260\346\215\256\345\210\206\346\236\220/page/2/index.html" "b/categories/Python-\346\225\260\346\215\256\345\210\206\346\236\220/page/2/index.html" index fb7c5fe1dd749601a8260638670d607502c2d79b..0cfc4e5cc3f3b2c69fb8b594e2dce85e0c261f71 100644 --- "a/categories/Python-\346\225\260\346\215\256\345\210\206\346\236\220/page/2/index.html" +++ "b/categories/Python-\346\225\260\346\215\256\345\210\206\346\236\220/page/2/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/categories/Python-\346\225\260\346\215\256\345\210\206\346\236\220/page/3/index.html" "b/categories/Python-\346\225\260\346\215\256\345\210\206\346\236\220/page/3/index.html" index 4463a15b05d69d480faab2fec075c33fd5a59ffa..6466309d25f345af213d290e6600a7cb7b483202 100644 --- "a/categories/Python-\346\225\260\346\215\256\345\210\206\346\236\220/page/3/index.html" +++ "b/categories/Python-\346\225\260\346\215\256\345\210\206\346\236\220/page/3/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/categories/Python3-\345\255\246\344\271\240\347\254\224\350\256\260/index.html" "b/categories/Python3-\345\255\246\344\271\240\347\254\224\350\256\260/index.html" index 45593f6a9536b23db760fdc2b9c6b7d75a774fb0..0675b3f22f572f6a21fab8c4c4712a7c40de2138 100644 --- "a/categories/Python3-\345\255\246\344\271\240\347\254\224\350\256\260/index.html" +++ "b/categories/Python3-\345\255\246\344\271\240\347\254\224\350\256\260/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/categories/Python3-\345\255\246\344\271\240\347\254\224\350\256\260/page/2/index.html" "b/categories/Python3-\345\255\246\344\271\240\347\254\224\350\256\260/page/2/index.html" index 287996ea49c14c16ebc07213cbe53872488bea19..90c55924046bd2e17a72fc6a26b183c5084c77ae 100644 --- "a/categories/Python3-\345\255\246\344\271\240\347\254\224\350\256\260/page/2/index.html" +++ "b/categories/Python3-\345\255\246\344\271\240\347\254\224\350\256\260/page/2/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/categories/Python3-\345\255\246\344\271\240\347\254\224\350\256\260/page/3/index.html" "b/categories/Python3-\345\255\246\344\271\240\347\254\224\350\256\260/page/3/index.html" index 51b680a38e334621f0788e7c1f636bbe201db126..a2a74311a68b04116adc8ef6a6b2ca40b341167f 100644 --- "a/categories/Python3-\345\255\246\344\271\240\347\254\224\350\256\260/page/3/index.html" +++ "b/categories/Python3-\345\255\246\344\271\240\347\254\224\350\256\260/page/3/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/categories/Python3-\345\255\246\344\271\240\347\254\224\350\256\260/page/4/index.html" "b/categories/Python3-\345\255\246\344\271\240\347\254\224\350\256\260/page/4/index.html" index e0eeeedf715b61ede3a7ac5acea870867d4c6758..3cb8843d179d72792ad259a956277c39a2a1bce2 100644 --- "a/categories/Python3-\345\255\246\344\271\240\347\254\224\350\256\260/page/4/index.html" +++ "b/categories/Python3-\345\255\246\344\271\240\347\254\224\350\256\260/page/4/index.html" @@ -110,7 +110,7 @@ - + @@ -670,10 +670,10 @@ diff --git "a/categories/Python3-\345\255\246\344\271\240\347\254\224\350\256\260/page/5/index.html" "b/categories/Python3-\345\255\246\344\271\240\347\254\224\350\256\260/page/5/index.html" index cd948993f80766425f5e3d6763f510715bc3f949..e119d7648b42c3430f4e3850f184119e10b2ca86 100644 --- "a/categories/Python3-\345\255\246\344\271\240\347\254\224\350\256\260/page/5/index.html" +++ "b/categories/Python3-\345\255\246\344\271\240\347\254\224\350\256\260/page/5/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/categories/Python3-\345\255\246\344\271\240\347\254\224\350\256\260/\345\237\272\347\241\200\345\255\246\344\271\240/index.html" "b/categories/Python3-\345\255\246\344\271\240\347\254\224\350\256\260/\345\237\272\347\241\200\345\255\246\344\271\240/index.html" index 8d59342eaec62d3ee7c19fb40c6f7a792734a82f..8760bd6b1abd3fb105b8c1d137676b406262b39d 100644 --- "a/categories/Python3-\345\255\246\344\271\240\347\254\224\350\256\260/\345\237\272\347\241\200\345\255\246\344\271\240/index.html" +++ "b/categories/Python3-\345\255\246\344\271\240\347\254\224\350\256\260/\345\237\272\347\241\200\345\255\246\344\271\240/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/categories/Python3-\345\255\246\344\271\240\347\254\224\350\256\260/\345\255\246\344\271\240\347\273\217\351\252\214/index.html" "b/categories/Python3-\345\255\246\344\271\240\347\254\224\350\256\260/\345\255\246\344\271\240\347\273\217\351\252\214/index.html" index e49d795869d019320fd2a576e975135a3b7b49a8..c6092b18c3310ec6d086360369d9ba15f8c05409 100644 --- "a/categories/Python3-\345\255\246\344\271\240\347\254\224\350\256\260/\345\255\246\344\271\240\347\273\217\351\252\214/index.html" +++ "b/categories/Python3-\345\255\246\344\271\240\347\254\224\350\256\260/\345\255\246\344\271\240\347\273\217\351\252\214/index.html" @@ -110,7 +110,7 @@ - + @@ -940,10 +940,10 @@ diff --git "a/categories/Python3-\345\255\246\344\271\240\347\254\224\350\256\260/\347\210\254\350\231\253\345\255\246\344\271\240/index.html" "b/categories/Python3-\345\255\246\344\271\240\347\254\224\350\256\260/\347\210\254\350\231\253\345\255\246\344\271\240/index.html" index fd8f2dcc0df9196c7f4aab6fcd29a738f94c755c..7ca7ca95809ca945b3e0ea45ecc89886de3e265b 100644 --- "a/categories/Python3-\345\255\246\344\271\240\347\254\224\350\256\260/\347\210\254\350\231\253\345\255\246\344\271\240/index.html" +++ "b/categories/Python3-\345\255\246\344\271\240\347\254\224\350\256\260/\347\210\254\350\231\253\345\255\246\344\271\240/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/categories/Python3-\345\255\246\344\271\240\347\254\224\350\256\260/\347\210\254\350\231\253\345\255\246\344\271\240/page/2/index.html" "b/categories/Python3-\345\255\246\344\271\240\347\254\224\350\256\260/\347\210\254\350\231\253\345\255\246\344\271\240/page/2/index.html" index 31b24334587f6aec16a9d063d780444a89608d7e..c110d17b0e451829ff2736b1f1e042c6c2fe22c4 100644 --- "a/categories/Python3-\345\255\246\344\271\240\347\254\224\350\256\260/\347\210\254\350\231\253\345\255\246\344\271\240/page/2/index.html" +++ "b/categories/Python3-\345\255\246\344\271\240\347\254\224\350\256\260/\347\210\254\350\231\253\345\255\246\344\271\240/page/2/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/categories/Python3-\345\255\246\344\271\240\347\254\224\350\256\260/\347\210\254\350\231\253\345\256\236\346\210\230/index.html" "b/categories/Python3-\345\255\246\344\271\240\347\254\224\350\256\260/\347\210\254\350\231\253\345\256\236\346\210\230/index.html" index 8b57bd3fa35bf91b333db03648440a298508d712..5b7ba4b9278b04cbfdf2cac8c3c0458079ce7010 100644 --- "a/categories/Python3-\345\255\246\344\271\240\347\254\224\350\256\260/\347\210\254\350\231\253\345\256\236\346\210\230/index.html" +++ "b/categories/Python3-\345\255\246\344\271\240\347\254\224\350\256\260/\347\210\254\350\231\253\345\256\236\346\210\230/index.html" @@ -110,7 +110,7 @@ - + diff --git a/categories/VPS/index.html b/categories/VPS/index.html index f37123060d9436cee864abcd7efa48719e15cb51..15eef6bfab77ecb9e2cb2796750a53420a83c469 100644 --- a/categories/VPS/index.html +++ b/categories/VPS/index.html @@ -110,7 +110,7 @@ - + diff --git "a/categories/WEB\345\211\215\347\253\257/index.html" "b/categories/WEB\345\211\215\347\253\257/index.html" index d67ba5a85e4611ada7a3ee4753e8e8c522a5625f..1522f68bdd99893d7b4f92beb92e32884d0c9f74 100644 --- "a/categories/WEB\345\211\215\347\253\257/index.html" +++ "b/categories/WEB\345\211\215\347\253\257/index.html" @@ -110,7 +110,7 @@ - + diff --git a/categories/index.html b/categories/index.html index b65f41eedfc674a957f5d0f31c64b7d6b8b87da1..0b275bff2523d581c1f38789e3deed9cf8ce4341 100644 --- a/categories/index.html +++ b/categories/index.html @@ -110,7 +110,7 @@ - + diff --git "a/categories/\345\233\276\345\272\212/index.html" "b/categories/\345\233\276\345\272\212/index.html" index 4b70c6d15df7b643743bf1281b78d69d4c0ef01d..2623b88a703057aa03962b2af41a910f34fd273f 100644 --- "a/categories/\345\233\276\345\272\212/index.html" +++ "b/categories/\345\233\276\345\272\212/index.html" @@ -110,7 +110,7 @@ - + diff --git a/comments/index.html b/comments/index.html index 4c77f638262ce4028381deb448485030f2a79995..84e89e74e7868cb15c3b9b6e35392196162cf084 100644 --- a/comments/index.html +++ b/comments/index.html @@ -110,7 +110,7 @@ - + diff --git a/content.json b/content.json index c56af94a6552e4fa95d0167b0856c512140a46cc..1f0f072810ef46477b9c723d75c6059ee8dad91a 100644 --- a/content.json +++ b/content.json @@ -1 +1 @@ -{"meta":{"title":"TRHX'S BLOG","subtitle":"一入 IT 深似海 从此学习无绝期","description":"TRHX 的个人博客;主攻 Python、爬虫、WEB前端、大数据、数据分析、数据可视化;求知若饥,虚心若愚,一入 IT 深似海,从此学习无绝期,记录毕生所学!","author":"TRHX","url":"https://www.itrhx.com"},"pages":[{"title":"","date":"2019-09-23T02:54:05.384Z","updated":"2019-09-23T02:54:05.384Z","comments":true,"path":"404.html","permalink":"https://www.itrhx.com/404.html","excerpt":"","text":"404 页面 | TRHX'S BLOG *{padding:0;margin:0}body{background-color:#eee;background-image:url(\"https://cdn.jsdelivr.net/gh/TRHX/CDN-for-itrhx.com@2.1.9/images/background/021.webp\");}#catch-the-cat{width:100%;margin-top:32px;text-align:center}#Copyright{text-align:center;font-size:15px;color:#000;margin:30px}#Copyright a{color:#000}#Copyright a:hover{color:#f00} 404 Not Found! 很抱歉,您访问的页面不存在,可能是输入地址有误或该地址已被删除! window.game = new CatchTheCatGame({ w: 11, h: 11, r: 20, backgroundColor: 0xffffff, parent: 'catch-the-cat', statusBarAlign: 'center', credit: 'www.itrhx.com' }); Copyright ©2018-2019 TRHX'S BLOG       鄂ICP备19003281号-3     点击返回首页"},{"title":"","date":"2020-02-28T07:25:47.215Z","updated":"2020-02-28T07:25:47.215Z","comments":true,"path":"2019-nCoV/index.html","permalink":"https://www.itrhx.com/2019-nCoV/index.html","excerpt":"","text":"全国新型冠状病毒实时分布图 body { height: 88vh; margin: 0; padding: 0; overflow: hidden; } h2 { text-align:center; margin: 20px 0 0 0; color: #0822B5; } h3 { text-align:center; margin: 20px 0 0 0; color: #0822B5; } a { color: #178b50; text-decoration: none; } a:hover { color: #d81d1b; text-decoration: none; } iframe { overflow:hidden; margin: 20px 0 0 0; border: none; } TRHX'S BLOG丨新冠肺炎实时疫情图(数据来源:新浪新闻) 相关链接:武汉新型冠状病毒防疫信息收集平台 丨 2019-nCoV 疫情信息导航网站"},{"title":"所有分类","date":"2019-04-27T16:28:11.064Z","updated":"2019-04-27T16:28:11.064Z","comments":true,"path":"categories/index.html","permalink":"https://www.itrhx.com/categories/index.html","excerpt":"","text":""},{"title":"所有标签","date":"2019-05-05T16:01:26.324Z","updated":"2019-05-05T16:01:26.324Z","comments":true,"path":"tags/index.html","permalink":"https://www.itrhx.com/tags/index.html","excerpt":"","text":""},{"title":"小伙伴们","date":"2020-08-18T01:45:12.586Z","updated":"2020-08-18T01:45:12.586Z","comments":false,"path":"friends/index.html","permalink":"https://www.itrhx.com/friends/index.html","excerpt":"","text":"名称:TRHX’S BLOG主页:https://www.itrhx.com/头像:https://www.itrhx.com/images/trhx.png标签:Python、爬虫、前端简介:求知若饥,虚心若愚! 由于目前友链数过多,所以暂停交换友链,敬请见谅!长期不能访问的站点将会被删除,若已恢复请留言告知!"},{"title":"","date":"2020-03-02T05:42:14.094Z","updated":"2020-03-02T05:42:14.094Z","comments":true,"path":"about/index.html","permalink":"https://www.itrhx.com/about/index.html","excerpt":"","text":"TRHX'S BLOG | ABOUT document.onkeydown = function () { if (window.event && window.event.keyCode == 123) { event.keyCode = 0; event.returnValue = false; return false; } }; 您的浏览器不支持audio标签,无法播放音乐! 江湖名称:TRHX 常驻之地:China~丨湖北丨武汉 初度之辰:1999 擅长领域:Python丨爬虫丨前端 技能丨Skill HTML/CSS/JS 65% C/C++ 20% JAVA 30% PYTHON 80% HEXO/GIT 90% PS/PR/AE 65% 简介丨Introduction ● 学历:武汉某本科,软件工程专业大三学生; ● 现况:自学 Python 中,网络爬虫方向; ● 目标:优秀前端工程师 or 网络爬虫工程师; ● 博客:好记性不如烂笔头,记录学习过程; ● 兴趣:酷爱编程,业余公路自行车手,WOT 玩家; ● 其他:虽然很菜,但是在努力学习中! 联系我丨Contact me Copyright © 2018-2020 TRHX'S BLOG. All rights reserved. if ('addEventListener' in window) { window.addEventListener('load', function () { document.body.className = document.body.className.replace(/\\bis-loading\\b/, ''); }); document.body.className += (navigator.userAgent.match(/(MSIE|rv:11\\.0)/) ? ' is-ie' : ''); } uniform mat4 uProjection; uniform mat4 uModelview; uniform vec3 uResolution; uniform vec3 uOffset; uniform vec3 uDOF; //x:focus distance, y:focus radius, z:max radius uniform vec3 uFade; //x:start distance, y:half distance, z:near fade start attribute vec3 aPosition; attribute vec3 aEuler; attribute vec2 aMisc; //x:size, y:fade varying vec3 pposition; varying float psize; varying float palpha; varying float pdist; //varying mat3 rotMat; varying vec3 normX; varying vec3 normY; varying vec3 normZ; varying vec3 normal; varying float diffuse; varying float specular; varying float rstop; varying float distancefade; void main(void) { // Projection is based on vertical angle vec4 pos = uModelview * vec4(aPosition + uOffset, 1.0); gl_Position = uProjection * pos; gl_PointSize = aMisc.x * uProjection[1][1] / -pos.z * uResolution.y * 0.5; pposition = pos.xyz; psize = aMisc.x; pdist = length(pos.xyz); palpha = smoothstep(0.0, 1.0, (pdist - 0.1) / uFade.z); vec3 elrsn = sin(aEuler); vec3 elrcs = cos(aEuler); mat3 rotx = mat3( 1.0, 0.0, 0.0, 0.0, elrcs.x, elrsn.x, 0.0, -elrsn.x, elrcs.x ); mat3 roty = mat3( elrcs.y, 0.0, -elrsn.y, 0.0, 1.0, 0.0, elrsn.y, 0.0, elrcs.y ); mat3 rotz = mat3( elrcs.z, elrsn.z, 0.0, -elrsn.z, elrcs.z, 0.0, 0.0, 0.0, 1.0 ); mat3 rotmat = rotx * roty * rotz; normal = rotmat[2]; mat3 trrotm = mat3( rotmat[0][0], rotmat[1][0], rotmat[2][0], rotmat[0][1], rotmat[1][1], rotmat[2][1], rotmat[0][2], rotmat[1][2], rotmat[2][2] ); normX = trrotm[0]; normY = trrotm[1]; normZ = trrotm[2]; const vec3 lit = vec3(0.6917144638660746, 0.6917144638660746, -0.20751433915982237); float tmpdfs = dot(lit, normal); if(tmpdfs < 0.0) { normal = -normal; tmpdfs = dot(lit, normal); } diffuse = 0.4 + tmpdfs; vec3 eyev = normalize(-pos.xyz); if(dot(eyev, normal) > 0.0) { vec3 hv = normalize(eyev + lit); specular = pow(max(dot(hv, normal), 0.0), 20.0); } else { specular = 0.0; } rstop = clamp((abs(pdist - uDOF.x) - uDOF.y) / uDOF.z, 0.0, 1.0); rstop = pow(rstop, 0.5); //-0.69315 = ln(0.5) distancefade = min(1.0, exp((uFade.x - pdist) * 0.69315 / uFade.y)); } #ifdef GL_ES //precision mediump float; precision highp float; #endif uniform vec3 uDOF; //x:focus distance, y:focus radius, z:max radius uniform vec3 uFade; //x:start distance, y:half distance, z:near fade start const vec3 fadeCol = vec3(0.08, 0.03, 0.06); varying vec3 pposition; varying float psize; varying float palpha; varying float pdist; //varying mat3 rotMat; varying vec3 normX; varying vec3 normY; varying vec3 normZ; varying vec3 normal; varying float diffuse; varying float specular; varying float rstop; varying float distancefade; float ellipse(vec2 p, vec2 o, vec2 r) { vec2 lp = (p - o) / r; return length(lp) - 1.0; } void main(void) { vec3 p = vec3(gl_PointCoord - vec2(0.5, 0.5), 0.0) * 2.0; vec3 d = vec3(0.0, 0.0, -1.0); float nd = normZ.z; //dot(-normZ, d); if(abs(nd) < 0.0001) discard; float np = dot(normZ, p); vec3 tp = p + d * np / nd; vec2 coord = vec2(dot(normX, tp), dot(normY, tp)); //angle = 15 degree const float flwrsn = 0.258819045102521; const float flwrcs = 0.965925826289068; mat2 flwrm = mat2(flwrcs, -flwrsn, flwrsn, flwrcs); vec2 flwrp = vec2(abs(coord.x), coord.y) * flwrm; float r; if(flwrp.x < 0.0) { r = ellipse(flwrp, vec2(0.065, 0.024) * 0.5, vec2(0.36, 0.96) * 0.5); } else { r = ellipse(flwrp, vec2(0.065, 0.024) * 0.5, vec2(0.58, 0.96) * 0.5); } if(r > rstop) discard; vec3 col = mix(vec3(1.0, 0.8, 0.75), vec3(1.0, 0.9, 0.87), r); float grady = mix(0.0, 1.0, pow(coord.y * 0.5 + 0.5, 0.35)); col *= vec3(1.0, grady, grady); col *= mix(0.8, 1.0, pow(abs(coord.x), 0.3)); col = col * diffuse + specular; col = mix(fadeCol, col, distancefade); float alpha = (rstop > 0.001)? (0.5 - r / (rstop * 2.0)) : 1.0; alpha = smoothstep(0.0, 1.0, alpha) * palpha; gl_FragColor = vec4(col * 0.5, alpha); } uniform vec3 uResolution; attribute vec2 aPosition; varying vec2 texCoord; varying vec2 screenCoord; void main(void) { gl_Position = vec4(aPosition, 0.0, 1.0); texCoord = aPosition.xy * 0.5 + vec2(0.5, 0.5); screenCoord = aPosition.xy * vec2(uResolution.z, 1.0); } #ifdef GL_ES //precision mediump float; precision highp float; #endif uniform vec2 uTimes; varying vec2 texCoord; varying vec2 screenCoord; void main(void) { vec3 col; float c; vec2 tmpv = texCoord * vec2(0.8, 1.0) - vec2(0.95, 1.0); c = exp(-pow(length(tmpv) * 1.8, 2.0)); col = mix(vec3(0.02, 0.0, 0.03), vec3(0.96, 0.98, 1.0) * 1.5, c); gl_FragColor = vec4(col * 0.5, 1.0); } #ifdef GL_ES //precision mediump float; precision highp float; #endif uniform sampler2D uSrc; uniform vec2 uDelta; varying vec2 texCoord; varying vec2 screenCoord; void main(void) { vec4 col = texture2D(uSrc, texCoord); gl_FragColor = vec4(col.rgb * 2.0 - vec3(0.5), 1.0); } #ifdef GL_ES //precision mediump float; precision highp float; #endif uniform sampler2D uSrc; uniform vec2 uDelta; uniform vec4 uBlurDir; //dir(x, y), stride(z, w) varying vec2 texCoord; varying vec2 screenCoord; void main(void) { vec4 col = texture2D(uSrc, texCoord); col = col + texture2D(uSrc, texCoord + uBlurDir.xy * uDelta); col = col + texture2D(uSrc, texCoord - uBlurDir.xy * uDelta); col = col + texture2D(uSrc, texCoord + (uBlurDir.xy + uBlurDir.zw) * uDelta); col = col + texture2D(uSrc, texCoord - (uBlurDir.xy + uBlurDir.zw) * uDelta); gl_FragColor = col / 5.0; } #ifdef GL_ES //precision mediump float; precision highp float; #endif uniform sampler2D uSrc; uniform vec2 uDelta; varying vec2 texCoord; varying vec2 screenCoord; void main(void) { gl_FragColor = texture2D(uSrc, texCoord); } uniform vec3 uResolution; attribute vec2 aPosition; varying vec2 texCoord; varying vec2 screenCoord; void main(void) { gl_Position = vec4(aPosition, 0.0, 1.0); texCoord = aPosition.xy * 0.5 + vec2(0.5, 0.5); screenCoord = aPosition.xy * vec2(uResolution.z, 1.0); } #ifdef GL_ES //precision mediump float; precision highp float; #endif uniform sampler2D uSrc; uniform sampler2D uBloom; uniform vec2 uDelta; varying vec2 texCoord; varying vec2 screenCoord; void main(void) { vec4 srccol = texture2D(uSrc, texCoord) * 2.0; vec4 bloomcol = texture2D(uBloom, texCoord); vec4 col; col = srccol + bloomcol * (vec4(1.0) + srccol); col *= smoothstep(1.0, 0.0, pow(length((texCoord - vec2(0.5)) * 2.0), 1.2) * 0.5); col = pow(col, vec4(0.45454545454545)); //(1.0 / 2.2) gl_FragColor = vec4(col.rgb, 1.0); gl_FragColor.a = 1.0; }"},{"title":"留言","date":"2020-04-16T04:39:29.101Z","updated":"2020-04-16T04:39:29.101Z","comments":true,"path":"comments/index.html","permalink":"https://www.itrhx.com/comments/index.html","excerpt":"","text":"                                    采用 Gitalk 评论系统,需使用 GitHub 账号登录,请尽情灌水吧!😉由于 Gitalk 调用的是 GitHub 的 issues,如果您参与了评论,可能别人评论您也会收到邮件,若不想再收到邮件,可以到 issues 页面取消订阅,取消后,如果您再次评论,也不会再收到邮件提醒!"},{"title":"","date":"2019-12-29T06:55:50.751Z","updated":"2019-12-29T06:55:50.751Z","comments":true,"path":"games/cat/index.html","permalink":"https://www.itrhx.com/games/cat/index.html","excerpt":"","text":"圈小猫 | TRHX'S BLOG body {background-color: #eeeeee} #catch-the-cat {width: 100%;text-align: center;} #footer{position:relative;clear:both;padding:10px 20px 40px 0;padding:10px 0;width:100%;text-align:center}#footer address{display:inline-block;padding:2px 10px;color:rgba(0,0,0,.5);font-style:normal} #footer a{color:rgba(0,0,0,.5);cursor:grab}#footer a:hover{border-bottom:1px dotted #00387d;color:#00387d} 游戏:《圈小猫》 window.game = new CatchTheCatGame({ w: 11, h: 11, r: 20, backgroundColor: 0xffffff, parent: 'catch-the-cat', statusBarAlign: 'center', credit: 'www.itrhx.com' }); Copyright © 2018-2019 TRHX'S BLOG |    鄂ICP备19003281号-3 |  中国互联网违法和不良信息举报中心 推荐使用1920*1080分辨率、谷歌或者火狐浏览器浏览此页 |  站点地图 |   |  RSS订阅 |  996.ICU |  申请友链 |  Powered By Hexo |  Hosted By GitHub Pages 部分资料来源于网络,版权属于其合法持有人,只供学习交流之用,非商务用途。如有侵犯您的权益,请及时告知删除。互动交流时请遵守理性,宽容,换位思考的原则。 document.onkeydown=function (e){ var currKey=0,evt=e||window.event; currKey=evt.keyCode||evt.which||evt.charCode; if (currKey == 123) { window.event.cancelBubble = true; window.event.returnValue = false; } }"},{"title":"","date":"2019-12-29T06:55:50.753Z","updated":"2019-12-29T06:55:50.753Z","comments":true,"path":"games/PacMan/index.html","permalink":"https://www.itrhx.com/games/PacMan/index.html","excerpt":"","text":"吃豆人 | TRHX'S BLOG body{background-color: #000} *{padding:0;margin:0;} .wrapper{ width: 960px; margin:0 auto; line-height:36px; text-align:center; color:#999; } canvas{display:block;background: #000;} .mod-botton{ height: 32px; padding: 15px 0; text-align: center; } #footer{position:relative;clear:both;padding:10px 20px 40px 0;padding:10px 0;width:100%;text-align:center}#footer address{display:inline-block;padding:2px 10px;color:rgba(255, 255, 255, 0.5);font-style:normal} #footer a{color:rgba(255, 255, 255, 0.5);cursor:grab}#footer a:hover{border-bottom:1px dotted #00387d;color:#00387d} 不支持画布 【按空格键开始、暂停或继续游戏,方向键移动吃豆人】 Copyright © 2018-2019 TRHX'S BLOG |    鄂ICP备19003281号-3 |  中国互联网违法和不良信息举报中心 推荐使用1920*1080分辨率、谷歌或者火狐浏览器浏览此页 |  站点地图 |   |  RSS订阅 |  996.ICU |  申请友链 |  Powered By Hexo |  Hosted By GitHub Pages 部分资料来源于网络,版权属于其合法持有人,只供学习交流之用,非商务用途。如有侵犯您的权益,请及时告知删除。互动交流时请遵守理性,宽容,换位思考的原则。 document.onkeydown=function (e){ var currKey=0,evt=e||window.event; currKey=evt.keyCode||evt.which||evt.charCode; if (currKey == 123) { window.event.cancelBubble = true; window.event.returnValue = false; } }"},{"title":"","date":"2019-12-29T06:55:50.751Z","updated":"2019-12-29T06:55:50.751Z","comments":true,"path":"games/2048/index.html","permalink":"https://www.itrhx.com/games/2048/index.html","excerpt":"","text":"2048 | TRHX'S BLOG 2048 使用方向键操作 New Game score:0 GAME OVER Copyright © 2018-2019 TRHX'S BLOG |    鄂ICP备19003281号-3 |  中国互联网违法和不良信息举报中心 推荐使用1920*1080分辨率、谷歌或者火狐浏览器浏览此页 |  站点地图 |   |  RSS订阅 |  996.ICU |  申请友链 |  Powered By Hexo |  Hosted By GitHub Pages 部分资料来源于网络,版权属于其合法持有人,只供学习交流之用,非商务用途。如有侵犯您的权益,请及时告知删除。互动交流时请遵守理性,宽容,换位思考的原则。 document.onkeydown=function (e){ var currKey=0,evt=e||window.event; currKey=evt.keyCode||evt.which||evt.charCode; if (currKey == 123) { window.event.cancelBubble = true; window.event.returnValue = false; } }"},{"title":"","date":"2019-12-29T06:55:50.754Z","updated":"2019-12-29T06:55:50.754Z","comments":true,"path":"games/piano/index.html","permalink":"https://www.itrhx.com/games/piano/index.html","excerpt":"","text":"网页版钢琴 | TRHX'S BLOG 网页版钢琴 qaz sx dc rfv gb hn jm ik, w e t y u 弹奏方法 使用鼠标左键点击钢琴键,或者键入钢琴键上输入的键盘字母。 Copyright © 2018-2019 TRHX'S BLOG |    鄂ICP备19003281号-3 |  中国互联网违法和不良信息举报中心 推荐使用1920*1080分辨率、谷歌或者火狐浏览器浏览此页 |  站点地图 |   |  RSS订阅 |  996.ICU |  申请友链 |  Powered By Hexo |  Hosted By GitHub Pages 部分资料来源于网络,版权属于其合法持有人,只供学习交流之用,非商务用途。如有侵犯您的权益,请及时告知删除。互动交流时请遵守理性,宽容,换位思考的原则。 document.onkeydown=function (e){ var currKey=0,evt=e||window.event; currKey=evt.keyCode||evt.which||evt.charCode; if (currKey == 123) { window.event.cancelBubble = true; window.event.returnValue = false; } }"},{"title":"","date":"2019-12-29T06:55:50.751Z","updated":"2019-12-29T06:55:50.751Z","comments":true,"path":"games/element/index.html","permalink":"https://www.itrhx.com/games/element/index.html","excerpt":"","text":"3D元素周期表 | TRHX'S BLOG html, body { height: 100%; } body { background-color: #000000; margin: 0; font-family: Helvetica, sans-serif;; overflow: hidden; } a { color: #ffffff; } #info { position: absolute; width: 100%; color: #ffffff; padding: 5px; font-family: Monospace; font-size: 13px; font-weight: bold; text-align: center; z-index: 1; } #menu { position: absolute; bottom: 20px; width: 100%; text-align: center; font-family: verdana,Tahoma,Arial,Hei,\"Microsoft Yahei\",SimHei; } .element { width: 120px; height: 160px; box-shadow: 0px 0px 12px rgba(0,255,255,0.5); border: 1px solid rgba(127,255,255,0.25); text-align: center; cursor: default; } .element:hover { box-shadow: 0px 0px 12px rgba(0,255,255,0.75); border: 1px solid rgba(127,255,255,0.75); } .element .number { position: absolute; top: 20px; right: 20px; font-size: 12px; color: rgba(127,255,255,0.75); } .element .symbol { position: absolute; top: 40px; left: 0px; right: 0px; font-size: 60px; font-weight: bold; color: rgba(255,255,255,0.75); text-shadow: 0 0 10px rgba(0,255,255,0.95); } .element .details { position: absolute; bottom: 15px; left: 0px; right: 0px; font-size: 12px; color: rgba(127,255,255,0.75); } button { color: rgba(127,255,255,0.75); background: transparent; outline: 1px solid rgba(127,255,255,0.75); border: 0px; padding: 5px 10px; cursor: pointer; } button:hover { background-color: rgba(0,255,255,0.5); } button:active { color: #000000; background-color: rgba(0,255,255,0.75); } 表面 球体 螺旋 网格 var table = [ \"H\", \"Hydrogen\", \"1.00794\", 1, 1, \"He\", \"Helium\", \"4.002602\", 18, 1, \"Li\", \"Lithium\", \"#6.941\", 1, 2, \"Be\", \"Beryllium\", \"9.012182\", 2, 2, \"B\", \"Boron\", \"#10.811\", 13, 2, \"C\", \"Carbon\", \"#12.0107\", 14, 2, \"N\", \"Nitrogen\", \"#14.0067\", 15, 2, \"O\", \"Oxygen\", \"#15.9994\", 16, 2, \"F\", \"Fluorine\", \"18.9984032\", 17, 2, \"Ne\", \"Neon\", \"#20.1797\", 18, 2, \"Na\", \"Sodium\", \"22.98976...\", 1, 3, \"Mg\", \"Magnesium\", \"#24.305\", 2, 3, \"Al\", \"Aluminium\", \"26.9815386\", 13, 3, \"Si\", \"Silicon\", \"#28.0855\", 14, 3, \"P\", \"Phosphorus\", \"30.973762\", 15, 3, \"S\", \"Sulfur\", \"#32.065\", 16, 3, \"Cl\", \"Chlorine\", \"#35.453\", 17, 3, \"Ar\", \"Argon\", \"#39.948\", 18, 3, \"K\", \"Potassium\", \"#39.948\", 1, 4, \"Ca\", \"Calcium\", \"#40.078\", 2, 4, \"Sc\", \"Scandium\", \"44.955912\", 3, 4, \"Ti\", \"Titanium\", \"#47.867\", 4, 4, \"V\", \"Vanadium\", \"#50.9415\", 5, 4, \"Cr\", \"Chromium\", \"#51.9961\", 6, 4, \"Mn\", \"Manganese\", \"54.938045\", 7, 4, \"Fe\", \"Iron\", \"#55.845\", 8, 4, \"Co\", \"Cobalt\", \"58.933195\", 9, 4, \"Ni\", \"Nickel\", \"#58.6934\", 10, 4, \"Cu\", \"Copper\", \"#63.546\", 11, 4, \"Zn\", \"Zinc\", \"#65.38\", 12, 4, \"Ga\", \"Gallium\", \"#69.723\", 13, 4, \"Ge\", \"Germanium\", \"#72.63\", 14, 4, \"As\", \"Arsenic\", \"#74.9216\", 15, 4, \"Se\", \"Selenium\", \"#78.96\", 16, 4, \"Br\", \"Bromine\", \"#79.904\", 17, 4, \"Kr\", \"Krypton\", \"#83.798\", 18, 4, \"Rb\", \"Rubidium\", \"#85.4678\", 1, 5, \"Sr\", \"Strontium\", \"#87.62\", 2, 5, \"Y\", \"Yttrium\", \"88.90585\", 3, 5, \"Zr\", \"Zirconium\", \"#91.224\", 4, 5, \"Nb\", \"Niobium\", \"92.90628\", 5, 5, \"Mo\", \"Molybdenum\", \"#95.96\", 6, 5, \"Tc\", \"Technetium\", \"(98)\", 7, 5, \"Ru\", \"Ruthenium\", \"#101.07\", 8, 5, \"Rh\", \"Rhodium\", \"#102.9055\", 9, 5, \"Pd\", \"Palladium\", \"#106.42\", 10, 5, \"Ag\", \"Silver\", \"#107.8682\", 11, 5, \"Cd\", \"Cadmium\", \"#112.411\", 12, 5, \"In\", \"Indium\", \"#114.818\", 13, 5, \"Sn\", \"Tin\", \"#118.71\", 14, 5, \"Sb\", \"Antimony\", \"#121.76\", 15, 5, \"Te\", \"Tellurium\", \"127.6\", 16, 5, \"I\", \"Iodine\", \"126.90447\", 17, 5, \"Xe\", \"Xenon\", \"#131.293\", 18, 5, \"Cs\", \"Caesium\", \"#132.9054\", 1, 6, \"Ba\", \"Barium\", \"#132.9054\", 2, 6, \"La\", \"Lanthanum\", \"138.90547\", 4, 9, \"Ce\", \"Cerium\", \"#140.116\", 5, 9, \"Pr\", \"Praseodymium\", \"140.90765\", 6, 9, \"Nd\", \"Neodymium\", \"#144.242\", 7, 9, \"Pm\", \"Promethium\", \"(145)\", 8, 9, \"Sm\", \"Samarium\", \"#150.36\", 9, 9, \"Eu\", \"Europium\", \"#151.964\", 10, 9, \"Gd\", \"Gadolinium\", \"#157.25\", 11, 9, \"Tb\", \"Terbium\", \"158.92535\", 12, 9, \"Dy\", \"Dysprosium\", \"162.5\", 13, 9, \"Ho\", \"Holmium\", \"164.93032\", 14, 9, \"Er\", \"Erbium\", \"#167.259\", 15, 9, \"Tm\", \"Thulium\", \"168.93421\", 16, 9, \"Yb\", \"Ytterbium\", \"#173.054\", 17, 9, \"Lu\", \"Lutetium\", \"#174.9668\", 18, 9, \"Hf\", \"Hafnium\", \"#178.49\", 4, 6, \"Ta\", \"Tantalum\", \"180.94788\", 5, 6, \"W\", \"Tungsten\", \"#183.84\", 6, 6, \"Re\", \"Rhenium\", \"#186.207\", 7, 6, \"Os\", \"Osmium\", \"#190.23\", 8, 6, \"Ir\", \"Iridium\", \"#192.217\", 9, 6, \"Pt\", \"Platinum\", \"#195.084\", 10, 6, \"Au\", \"Gold\", \"196.966569\", 11, 6, \"Hg\", \"Mercury\", \"#200.59\", 12, 6, \"Tl\", \"Thallium\", \"#204.3833\", 13, 6, \"Pb\", \"Lead\", \"207.2\", 14, 6, \"Bi\", \"Bismuth\", \"#208.9804\", 15, 6, \"Po\", \"Polonium\", \"(209)\", 16, 6, \"At\", \"Astatine\", \"(210)\", 17, 6, \"Rn\", \"Radon\", \"(222)\", 18, 6, \"Fr\", \"Francium\", \"(223)\", 1, 7, \"Ra\", \"Radium\", \"(226)\", 2, 7, \"Ac\", \"Actinium\", \"(227)\", 4, 10, \"Th\", \"Thorium\", \"232.03806\", 5, 10, \"Pa\", \"Protactinium\", \"#231.0588\", 6, 10, \"U\", \"Uranium\", \"238.02891\", 7, 10, \"Np\", \"Neptunium\", \"(237)\", 8, 10, \"Pu\", \"Plutonium\", \"(244)\", 9, 10, \"Am\", \"Americium\", \"(243)\", 10, 10, \"Cm\", \"Curium\", \"(247)\", 11, 10, \"Bk\", \"Berkelium\", \"(247)\", 12, 10, \"Cf\", \"Californium\", \"(251)\", 13, 10, \"Es\", \"Einstenium\", \"(252)\", 14, 10, \"Fm\", \"Fermium\", \"(257)\", 15, 10, \"Md\", \"Mendelevium\", \"(258)\", 16, 10, \"No\", \"Nobelium\", \"(259)\", 17, 10, \"Lr\", \"Lawrencium\", \"(262)\", 18, 10, \"Rf\", \"Rutherfordium\", \"(267)\", 4, 7, \"Db\", \"Dubnium\", \"(268)\", 5, 7, \"Sg\", \"Seaborgium\", \"(271)\", 6, 7, \"Bh\", \"Bohrium\", \"(272)\", 7, 7, \"Hs\", \"Hassium\", \"(270)\", 8, 7, \"Mt\", \"Meitnerium\", \"(276)\", 9, 7, \"Ds\", \"Darmstadium\", \"(281)\", 10, 7, \"Rg\", \"Roentgenium\", \"(280)\", 11, 7, \"Cn\", \"Copernicium\", \"(285)\", 12, 7, \"Uut\", \"Unutrium\", \"(284)\", 13, 7, \"Fl\", \"Flerovium\", \"(289)\", 14, 7, \"Uup\", \"Ununpentium\", \"(288)\", 15, 7, \"Lv\", \"Livermorium\", \"(293)\", 16, 7, \"Uus\", \"Ununseptium\", \"(294)\", 17, 7, \"Uuo\", \"Ununoctium\", \"(294)\", 18, 7 ]; var camera, scene, renderer; var controls; var objects = []; var targets = { table: [], sphere: [], helix: [], grid: [] }; init(); animate(); function init() { camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 10000 ); camera.position.z = 3000; scene = new THREE.Scene(); // table for ( var i = 0; i < table.length; i += 5 ) { var element = document.createElement( 'div' ); element.className = 'element'; element.style.backgroundColor = 'rgba(0,127,127,' + ( Math.random() * 0.5 + 0.25 ) + ')'; var number = document.createElement( 'div' ); number.className = 'number'; number.textContent = (i/5) + 1; element.appendChild( number ); var symbol = document.createElement( 'div' ); symbol.className = 'symbol'; symbol.textContent = table[ i ]; element.appendChild( symbol ); var details = document.createElement( 'div' ); details.className = 'details'; details.innerHTML = table[ i + 1 ] + '' + table[ i + 2 ]; element.appendChild( details ); var object = new THREE.CSS3DObject( element ); object.position.x = Math.random() * 4000 - 2000; object.position.y = Math.random() * 4000 - 2000; object.position.z = Math.random() * 4000 - 2000; scene.add( object ); objects.push( object ); // var object = new THREE.Object3D(); object.position.x = ( table[ i + 3 ] * 140 ) - 1330; object.position.y = - ( table[ i + 4 ] * 180 ) + 990; targets.table.push( object ); } // sphere var vector = new THREE.Vector3(); for ( var i = 0, l = objects.length; i < l; i ++ ) { var phi = Math.acos( -1 + ( 2 * i ) / l ); var theta = Math.sqrt( l * Math.PI ) * phi; var object = new THREE.Object3D(); object.position.x = 800 * Math.cos( theta ) * Math.sin( phi ); object.position.y = 800 * Math.sin( theta ) * Math.sin( phi ); object.position.z = 800 * Math.cos( phi ); vector.copy( object.position ).multiplyScalar( 2 ); object.lookAt( vector ); targets.sphere.push( object ); } // helix var vector = new THREE.Vector3(); for ( var i = 0, l = objects.length; i < l; i ++ ) { var phi = i * 0.175 + Math.PI; var object = new THREE.Object3D(); object.position.x = 900 * Math.sin( phi ); object.position.y = - ( i * 8 ) + 450; object.position.z = 900 * Math.cos( phi ); vector.x = object.position.x * 2; vector.y = object.position.y; vector.z = object.position.z * 2; object.lookAt( vector ); targets.helix.push( object ); } // grid for ( var i = 0; i < objects.length; i ++ ) { var object = new THREE.Object3D(); object.position.x = ( ( i % 5 ) * 400 ) - 800; object.position.y = ( - ( Math.floor( i / 5 ) % 5 ) * 400 ) + 800; object.position.z = ( Math.floor( i / 25 ) ) * 1000 - 2000; targets.grid.push( object ); } // renderer = new THREE.CSS3DRenderer(); renderer.setSize( window.innerWidth, window.innerHeight ); renderer.domElement.style.position = 'absolute'; document.getElementById( 'container' ).appendChild( renderer.domElement ); // controls = new THREE.TrackballControls( camera, renderer.domElement ); controls.rotateSpeed = 0.5; controls.minDistance = 500; controls.maxDistance = 6000; controls.addEventListener( 'change', render ); var button = document.getElementById( 'table' ); button.addEventListener( 'click', function ( event ) { transform( targets.table, 2000 ); }, false ); var button = document.getElementById( 'sphere' ); button.addEventListener( 'click', function ( event ) { transform( targets.sphere, 2000 ); }, false ); var button = document.getElementById( 'helix' ); button.addEventListener( 'click', function ( event ) { transform( targets.helix, 2000 ); }, false ); var button = document.getElementById( 'grid' ); button.addEventListener( 'click', function ( event ) { transform( targets.grid, 2000 ); }, false ); transform( targets.table, 5000 ); // window.addEventListener( 'resize', onWindowResize, false ); } function transform( targets, duration ) { TWEEN.removeAll(); for ( var i = 0; i < objects.length; i ++ ) { var object = objects[ i ]; var target = targets[ i ]; new TWEEN.Tween( object.position ) .to( { x: target.position.x, y: target.position.y, z: target.position.z }, Math.random() * duration + duration ) .easing( TWEEN.Easing.Exponential.InOut ) .start(); new TWEEN.Tween( object.rotation ) .to( { x: target.rotation.x, y: target.rotation.y, z: target.rotation.z }, Math.random() * duration + duration ) .easing( TWEEN.Easing.Exponential.InOut ) .start(); } new TWEEN.Tween( this ) .to( {}, duration * 2 ) .onUpdate( render ) .start(); } function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize( window.innerWidth, window.innerHeight ); render(); } function animate() { requestAnimationFrame( animate ); TWEEN.update(); controls.update(); } function render() { renderer.render( scene, camera ); } document.onkeydown=function (e){ var currKey=0,evt=e||window.event; currKey=evt.keyCode||evt.which||evt.charCode; if (currKey == 123) { window.event.cancelBubble = true; window.event.returnValue = false; } }"},{"title":"","date":"2019-12-29T06:55:50.754Z","updated":"2019-12-29T06:55:50.754Z","comments":true,"path":"games/gobang/index.html","permalink":"https://www.itrhx.com/games/gobang/index.html","excerpt":"","text":"五子棋 | TRHX'S BLOG canvas { display: block; margin: 60px auto; box-shadow: -2px -2px 2px #efefef, 5px 5px 5px #b9b9b9; cursor: pointer; } .btn-wrap { display: flex; flex-direction: row; justify-content: center; } .btn-wrap div { margin: 0 10px; } div>span { display: inline-block; padding: 10px 20px; color: #fff; background-color: #6496ED; border-radius: 5px; cursor: pointer; } div.unable span { background: #D6D6D4; color: #adacaa; } #result-wrap { text-align: center; margin:50px 0 0 0; } #footer{position:relative;clear:both;padding:10px 20px 40px 0;padding:10px 0;width:100%;text-align:center}#footer address{display:inline-block;padding:2px 10px;color:rgba(0,0,0,.5);font-style:normal} #footer a{color:rgba(0,0,0,.5);cursor:grab}#footer a:hover{border-bottom:1px dotted #00387d;color:#00387d} 人机五子棋对弈 重新开始 悔棋 撤销悔棋 var over = false; var me = true; //我 var _nowi = 0, _nowj = 0; //记录自己下棋的坐标 var _compi = 0, _compj = 0; //记录计算机当前下棋的坐标 var _myWin = [], _compWin = []; //记录我,计算机赢的情况 var backAble = false, returnAble = false; var resultTxt = document.getElementById('result-wrap'); var chressBord = []; //棋盘 for (var i = 0; i < 15; i++) { chressBord[i] = []; for (var j = 0; j < 15; j++) { chressBord[i][j] = 0; } } //赢法的统计数组 var myWin = []; var computerWin = []; //赢法数组 var wins = []; for (var i = 0; i < 15; i++) { wins[i] = []; for (var j = 0; j < 15; j++) { wins[i][j] = []; } } var count = 0; //赢法总数 //横线赢法 for (var i = 0; i < 15; i++) { for (var j = 0; j < 11; j++) { for (var k = 0; k < 5; k++) { wins[i][j + k][count] = true; } count++; } } //竖线赢法 for (var i = 0; i < 15; i++) { for (var j = 0; j < 11; j++) { for (var k = 0; k < 5; k++) { wins[j + k][i][count] = true; } count++; } } //正斜线赢法 for (var i = 0; i < 11; i++) { for (var j = 0; j < 11; j++) { for (var k = 0; k < 5; k++) { wins[i + k][j + k][count] = true; } count++; } } //反斜线赢法 for (var i = 0; i < 11; i++) { for (var j = 14; j > 3; j--) { for (var k = 0; k < 5; k++) { wins[i + k][j - k][count] = true; } count++; } } // debugger; for (var i = 0; i < count; i++) { myWin[i] = 0; _myWin[i] = 0; computerWin[i] = 0; _compWin[i] = 0; } var chess = document.getElementById(\"chess\"); var context = chess.getContext('2d'); context.strokeStyle = '#bfbfbf'; //边框颜色 var backbtn = document.getElementById(\"goback\"); var returnbtn = document.getElementById(\"return\"); window.onload = function () { drawChessBoard(); // 画棋盘 } document.getElementById(\"restart\").onclick = function () { window.location.reload(); } // 我,下棋 chess.onclick = function (e) { if (over) { return; } if (!me) { return; } // 悔棋功能可用 backbtn.className = backbtn.className.replace(new RegExp(\"(\\\\s|^)unable(\\\\s|$)\"), \" \"); var x = e.offsetX; var y = e.offsetY; var i = Math.floor(x / 30); var j = Math.floor(y / 30); _nowi = i; _nowj = j; if (chressBord[i][j] == 0) { oneStep(i, j, me); chressBord[i][j] = 1; //我,已占位置 for (var k = 0; k < count; k++) { // 将可能赢的情况都加1 if (wins[i][j][k]) { // debugger; myWin[k]++; _compWin[k] = computerWin[k]; computerWin[k] = 6; //这个位置对方不可能赢了 if (myWin[k] == 5) { // window.alert('你赢了'); resultTxt.innerHTML = '恭喜,你赢了!'; over = true; } } } if (!over) { me = !me; computerAI(); } } } // 悔棋 backbtn.onclick = function (e) { if (!backAble) { return; } over = false; me = true; // resultTxt.innerHTML = 'emmmm,悔棋中'; // 撤销悔棋功能可用 returnbtn.className = returnbtn.className.replace(new RegExp(\"(\\\\s|^)unable(\\\\s|$)\"), \" \"); // 我,悔棋 chressBord[_nowi][_nowj] = 0; //我,已占位置 还原 minusStep(_nowi, _nowj); //销毁棋子 for (var k = 0; k < count; k++) { // 将可能赢的情况都减1 if (wins[_nowi][_nowj][k]) { myWin[k]--; computerWin[k] = _compWin[k]; //这个位置对方可能赢 } }// 计算机相应的悔棋 chressBord[_compi][_compj] = 0; //计算机,已占位置 还原 minusStep(_compi, _compj); //销毁棋子 for (var k = 0; k < count; k++) { // 将可能赢的情况都减1 if (wins[_compi][_compj][k]) { computerWin[k]--; myWin[k] = _myWin[i]; //这个位置对方可能赢 } } resultTxt.innerHTML = '--人机五子棋--'; returnAble = true; backAble = false; } // 撤销悔棋 returnbtn.onclick = function (e) { if (!returnAble) { return; } // 我,撤销悔棋 chressBord[_nowi][_nowj] = 1; //我,已占位置 oneStep(_nowi, _nowj, me); for (var k = 0; k < count; k++) { if (wins[_nowi][_nowj][k]) { myWin[k]++; _compWin[k] = computerWin[k]; computerWin[k] = 6; //这个位置对方不可能赢 } if (myWin[k] == 5) { resultTxt.innerHTML = '恭喜,你赢了!'; over = true; } }// 计算机撤销相应的悔棋 chressBord[_compi][_compj] = 2; //计算机,已占位置 oneStep(_compi, _compj, false); for (var k = 0; k < count; k++) { // 将可能赢的情况都减1 if (wins[_compi][_compj][k]) { computerWin[k]++; _myWin[k] = myWin[k]; myWin[k] = 6; //这个位置对方不可能赢 } if (computerWin[k] == 5) { resultTxt.innerHTML = '很遗憾,计算机赢了,继续加油哦!'; over = true; } } returnbtn.className += ' ' + 'unable'; returnAble = false; backAble = true; } // 计算机下棋 var computerAI = function () { var myScore = []; var computerScore = []; var max = 0; var u = 0, v = 0; for (var i = 0; i < 15; i++) { myScore[i] = []; computerScore[i] = []; for (var j = 0; j < 15; j++) { myScore[i][j] = 0; computerScore[i][j] = 0; } } for (var i = 0; i < 15; i++) { for (var j = 0; j < 15; j++) { if (chressBord[i][j] == 0) { for (var k = 0; k < count; k++) { if (wins[i][j][k]) { if (myWin[k] == 1) { myScore[i][j] += 200; } else if (myWin[k] == 2) { myScore[i][j] += 400; } else if (myWin[k] == 3) { myScore[i][j] += 2000; } else if (myWin[k] == 4) { myScore[i][j] += 10000; } if (computerWin[k] == 1) { computerScore[i][j] += 220; } else if (computerWin[k] == 2) { computerScore[i][j] += 420; } else if (computerWin[k] == 3) { computerScore[i][j] += 2100; } else if (computerWin[k] == 4) { computerScore[i][j] += 20000; } } } if (myScore[i][j] > max) { max = myScore[i][j]; u = i; v = j; } else if (myScore[i][j] == max) { if (computerScore[i][j] > computerScore[u][v]) { u = i; v = j; } } if (computerScore[i][j] > max) { max = computerScore[i][j]; u = i; v = j; } else if (computerScore[i][j] == max) { if (myScore[i][j] > myScore[u][v]) { u = i; v = j; } } } } } _compi = u; _compj = v; oneStep(u, v, false); chressBord[u][v] = 2;//计算机占据位置 for (var k = 0; k < count; k++) { if (wins[u][v][k]) { computerWin[k]++; _myWin[k] = myWin[k]; myWin[k] = 6; //这个位置对方不可能赢了 if (computerWin[k] == 5) { resultTxt.innerHTML = '很遗憾,计算机赢了,继续加油哦!'; over = true; } } } if (!over) { me = !me; } backAble = true; returnAble = false; var hasClass = new RegExp('unable').test(' ' + returnbtn.className + ' '); if (!hasClass) { returnbtn.className += ' ' + 'unable'; } } //绘画棋盘 var drawChessBoard = function () { for (var i = 0; i < 15; i++) { context.moveTo(15 + i * 30, 15); context.lineTo(15 + i * 30, 435); context.stroke(); context.moveTo(15, 15 + i * 30); context.lineTo(435, 15 + i * 30); context.stroke(); } } //画棋子 var oneStep = function (i, j, me) { context.beginPath(); context.arc(15 + i * 30, 15 + j * 30, 13, 0, 2 * Math.PI); // 画圆 context.closePath(); //渐变 var gradient = context.createRadialGradient(15 + i * 30 + 2, 15 + j * 30 - 2, 13, 15 + i * 30 + 2, 15 + j * 30 - 2, 0); if (me) { gradient.addColorStop(0, '#0a0a0a'); gradient.addColorStop(1, '#636766'); } else { gradient.addColorStop(0, '#d1d1d1'); gradient.addColorStop(1, '#f9f9f9'); } context.fillStyle = gradient; context.fill(); } //销毁棋子 var minusStep = function (i, j) { //擦除该圆 context.clearRect((i) * 30, (j) * 30, 30, 30); // 重画该圆周围的格子 context.beginPath(); context.moveTo(15 + i * 30, j * 30); context.lineTo(15 + i * 30, j * 30 + 30); context.moveTo(i * 30, j * 30 + 15); context.lineTo((i + 1) * 30, j * 30 + 15); context.stroke(); } Copyright © 2018-2019 TRHX'S BLOG |    鄂ICP备19003281号-3 |  中国互联网违法和不良信息举报中心 推荐使用1920*1080分辨率、谷歌或者火狐浏览器浏览此页 |  站点地图 |   |  RSS订阅 |  996.ICU |  申请友链 |  Powered By Hexo |  Hosted By GitHub Pages 部分资料来源于网络,版权属于其合法持有人,只供学习交流之用,非商务用途。如有侵犯您的权益,请及时告知删除。互动交流时请遵守理性,宽容,换位思考的原则。 document.onkeydown=function (e){ var currKey=0,evt=e||window.event; currKey=evt.keyCode||evt.which||evt.charCode; if (currKey == 123) { window.event.cancelBubble = true; window.event.returnValue = false; } }"},{"title":"","date":"2019-12-29T06:55:50.751Z","updated":"2019-12-29T06:55:50.751Z","comments":true,"path":"box/about/index.html","permalink":"https://www.itrhx.com/box/about/index.html","excerpt":"","text":"关于本页丨TRHX'S BLOG 关于本页 本页面收集了比较常用或者实用的网站,相当于一个小小的导航页面。 整个页面由 Viggo 开发,完全开源,如果你也喜欢,欢迎去其 Github 点亮 star。 关于 Viggo Designer. Viggo. Full-time UI designer with an enduring interest in Coding. 一个全职的用户界面设计师,优秀的前端开发工程师,擅长 WEB 开发、WEB 设计、UI/UX 设计,对编程,拍照和单车有着持久的兴趣,生活在广州;如果您想招收此方面的人才,Viggo 无疑是一个很好的选择。 关于 TRHX TRHX 在校本科软件工程大三学生,主攻 Python、爬虫和前端。 一个想成为大佬的在校本科生,倾向于 Python、网络爬虫、数据分析、数据可视化、WEB 前端方面的学习,热爱编程、单车和户外运动,坚信好记性不如烂笔头,业精于勤荒于嬉,行成于思毁于随。 COPYRIGHT 2018 - 2020 WEBSTACK 丨 DESIGNED BY VIGGO 丨 CHANGED BY TRHX"},{"title":"","date":"2020-02-27T12:02:54.700Z","updated":"2020-02-27T12:02:54.700Z","comments":true,"path":"box/index.html","permalink":"https://www.itrhx.com/box/index.html","excerpt":"","text":"百宝箱丨TRHX'S BLOG 开发社区 代码托管 语言文档 技能训练 在线平台 高校平台 游戏编程 HOT Pythoner 文档资料 博客收藏 学习资源 组织社区 爬虫相关 HOT 学习教程 在线视频 博客论坛 学习平台 常用工具 站长工具 HOT IT工具箱 文件处理 HOT 设计素材 效率软件 HOT 服务平台 云服务商 众包平台 站内游戏 HOT 更多导航 关于本页 隐藏/显示侧边栏 博客首页 友情链接 评论留言 关于博主 (function(a,h,g,f,e,d,c,b){b=function(){d=h.createElement(g);c=h.getElementsByTagName(g)[0];d.src=e;d.charset=\"utf-8\";d.async=1;c.parentNode.insertBefore(d,c)};a[\"SeniverseWeatherWidgetObject\"]=f;a[f]||(a[f]=function(){(a[f].q=a[f].q||[]).push(arguments)});a[f].l=+new Date();if(a.attachEvent){a.attachEvent(\"onload\",b)}else{a.addEventListener(\"load\",b,false)}}(window,document,\"script\",\"SeniverseWeatherWidget\",\"//cdn.sencdn.com/widget2/static/js/bundle.js?t=\"+parseInt((new Date().getTime() / 100000000).toString(),10))); window.SeniverseWeatherWidget('show', { flavor: \"slim\", location: \"WX4FBXXFKE4F\", geolocation: true, language: \"auto\", unit: \"c\", theme: \"auto\", token: \"a39cd5a0-4024-4cb2-85c6-0250317058db\", hover: \"enabled\", container: \"tp-weather-widget\" }) 开发社区 Stack Overflow 全球最受程序员欢迎的开发社区 CSDN 全球最大中文IT社区,为IT专业技术人员提供最全面的信息传播和服务平台 博客园 代码改变世界 V2EX V2EX = way to explore 掘金 一个帮助开发者成长的社区 SegmentFault 改变并提升人们获取知识的方式和效率,帮助更多的开发者获得成长与成功 开源中国 国内最大的开源技术社区 ITeye ITeye软件开发交流社区 - Java编程 Spring框架 Ajax技术 agile敏捷软件开发 ruby on rails实践 51CTO 技术成就梦想 ITPUB 全球最大的学习分享平台 知乎 国内最受欢迎的知识性问答社区 简书 创作你的创作 云+社区 来自腾讯的开发者技术分享社区 云栖社区 阿里云面向开发者的开放型技术平台 代码托管 Github 全球最大的面向开源及私有软件项目的托管平台 Gitlab 支持无限的公有项目和私有项目的代码托管平台 Bitbucket 同时支持 Git 和 Mercurial 这两个版本控制软件,免费的私有仓库,支持5人以内的合作开发 SourceForge 又称 SF.net,是开源软件开发者进行开发管理的集中式场所 Coding 国内首个一站式云端软件服务平台 Gitee 国内最大的开源社区 OSChina 的代码托管平台 阿里云代码托管 阿里云旗下代码托管平台 百度效率云 百度云旗下的 Git 代码托管平台 语言文档 Zeal 脱机文档浏览器,包含196种语言API文档,支持Windows、Linux和macOS Dash 适用于Mac OS平台的软件编程文档管理工具,可以浏览API文档,以及管理代码片段工具。自带了丰富的API文档,涉及各种主流的编程语言和框架 DevDocs 在快速,有条理和可搜索的界面中结合了多个API文档,可以在移动设备上离线运行,并且可以安装在Chrome上 C/C++ C/C++ API 文档 C# C# API 文档 Java Java API 文档 .NET .NET API 文档 PHP PHP API 文档 JavaScript JavaScript API 文档 Python Python API 文档 Android Android API 文档 iOS iOS API 文档 SQL SQL API 文档 Swift Swift API 文档 Ruby Ruby API 文档 GO GO API 文档 R R API 文档 MATLAB MATLAB API 文档 Node.js Node.js API 文档 HTML HTML API 文档 CSS CSS API 文档 Redis Redis API 文档 MongoDB MongoDB API 文档 Django Django API 文档 在线平台 LeetCode 全球极客挚爱的技术成长平台 Topcoder 全世界规模最大的程序竞赛网站,也会有一些算法竞赛,适合一些高端的或者搞ACM的,也会举办一些比赛 Codeforces 俄罗斯最大的算法比赛网站 Hihocoder 技术团队来自原北京大学POJ (PKU Online Judge)开发团队,收集了全球范围内很多地区、高校举办的比赛试题, 提供365天*24小时的在线提交、评判程序的服务 LintCode 被称作中文版的leetcode,也是可以做为编程能力提升的一个中转站 SPOJ 波兰的算法刷题网站 NEUQ OJ 一个在线的判题平台 洛谷 创办于2013年,致力于为参加noip、noi、acm的选手提供清爽、快捷的编程体验 牛客网 中国最大的IT题库 C语言网 在这里可以参加包括ACM、NOI在内的各种C/C++/java程序比赛,也可以DIY举办各类程序比赛活动! 计蒜客 计蒜客OI题库致力于为参加noi、noip、信息学竞赛的选手提供优秀的Online Judge系统 高校平台 POJ 北京大学程序在线评测系统 FDU OJ 复旦大学程序在线评测系统 TJ OJ 同济大学程序在线评测系统 USTC OJ 中国科学技术大学程序在线评测系统 ZOJ 浙江大学程序在线评测系统 HDU OJ 杭州电子科技大学程序在线评测系统 CSU-ACM 中南大学程序在线评测系统 HOJX 哈尔滨工业大学程序在线评测系统 HRBUST OJ 哈尔滨理工大学程序在线评测系统 PowerOJ 西南科技大学程序在线评测系统 SCU OJ 四川大学程序在线评测系统 FZU CoidngOJ 福州大学程序在线评测系统 NBUT OJ 宁波工程学院程序在线评测系统 Lutece 电子科技大学程序在线评测系统 武汉大学 ACM 协会 武汉大学 ACM 协会 ZJUT OJ 浙江工业大学程序在线评测系统 游戏编程 CheckiO 面向初学者和高级程序员的编码游戏,使用Python和JavaScript解决棘手的挑战和有趣的任务,从而提高您的编码技能 Coding Games 支持包括PHP、C、JavaScript在内的20多种编程语言。用户界面功能强大,可以定制 Codewars 一个外国的在线练习编程的网站,做题的过程类似打怪,做题升级,而且可以看到别人的解法,里面有很多巧妙的写法可以学习 CodeCombat 一个面向学生的游戏和CS学习平台。这是一个社区项目,有数百玩家自愿提供支持。支持语言包括Java、JS、Python、Lua、CoffeeScript Screeps 在游戏中学习JavaScript。世界上第一款针对程序员的MMO沙盒游戏 VIM Adventures 玩游戏的时候学VIM Cyber-Dojo 一个提供给程序员们练习写程序的地方。支持语言包括JavaScript、Java、Python、PHP、Ruby和很多其他语言 Elevator Saga 电梯编程游戏,跟随关卡解决所有挑站,使用语言为JavaScript Ruby Quiz 一个Ruby程序员提供的每周编程挑战项目 hacker.org 这项挑战由一系列本设计来强化你黑客技巧的解密、诡计、测试、烧脑环节组成。想要通关本系列,你必须学会解密、编码、渗透 Ruby Warrior 玩游戏学Ruby,通过Ruby脚本来控制一个Warrior通过每一关,每一关的代码难度都会有所增加,使玩家逐渐了解Ruby基本的函数、控制、变量、数组等语言特性的用法 文档资料 Python 官方文档 Python 官方文档 Python 标准库 Python 标准库 Python Requests Python Requests 文档 Python Urllib Python Urllib 文档 Python Selenium Python Selenium 中文翻译文档 正则表达式 Python 正则表达式官方文档 Beautiful Soup Beautiful Soup 文档 Scrapy Scrapy 爬虫框架官方文档 PySpider PySpider 爬虫框架官方文档 Matplotlib Matplotlib 2D绘图库 官方中文文档 Numpy Numpy 科学计算 官方中文文档 Pandas Pandas 结构化数据分析 官方中文文档 博客收藏 廖雪峰 廖雪峰的官方网站 - 研究互联网产品和技术,提供原创中文精品教程 崔庆才 崔庆才的个人博客,专注PHP,Python,爬虫,深度学习,机器学习,数据分析 莫烦Python 专注Python、机器学习、深度学习 唐松 专注Python网络爬虫, 数据科学, 数据挖掘, 数据分析 捕蛇者说 编程、程序员、Python FxxkPython 学习python的正确姿势 wistbean Python 大佬 Piglei Python 大佬 TendCode Python 大佬 追梦人物的博客 Python Django 大佬 the5fire 《Django企业开发实战》作者,关注Python、Django、Vim、Linux、Web开发 小明明S À DOMICILE 《Python Web开发实战》作者,Python 大佬 Python之禅 Python 大佬 Python 知识圈 Python知识圈 - 实用的Python教程网站 Python 教程网 小詹学Python,专注Python学习 烂笔头 j_hao104 Python大佬 咸鱼日常 专注Python爬虫,有许多JS逆向文章 AnSheng Python 全栈大佬 夏溪辰 云栖社区特邀爬虫工程师,Python大佬 高级农民工 Python大佬 云爬虫技术研究笔记 Lateautumn4lin 爬虫开发工程师,多年反爬虫破解经验,沉迷数据分析和黑客增长,CSDN博客专家,华为云享专家 云爬虫技术研究笔记(CSDN) Lateautumn4lin 爬虫开发工程师,多年反爬虫破解经验,沉迷数据分析和黑客增长,CSDN博客专家,华为云享专家 Jack Cui CSDN博客专家,Python 大佬 学习资源 Python爬虫人工智能学习教程 Python爬虫人工智能学习教程分享 Python 中文学习大本营 Python 中文学习大本营 Python 资源大全中文版 Python 资源大全中文版 爱湃森 各种 Python 教程 组织社区 PyChina Python 中国社区 PyCon China 中国 Python 开发者大会 蠎周刊 蠎周刊 - 汇集全球蠎事儿 爬虫相关 镀金的天空 GlidedSky 镀金的天空,在线爬虫练习题库 夜幕爬虫安全论坛 一个专注于爬虫与 PC/Web/ 移动端安全领域技术交流的社区,社区由夜幕团队 NightTeam 创办,旨在提升开发者对爬虫与软件安全防护的理解 西刺免费代理IP 每日更新免费HTTP代理,所有代理均为6675端口高匿代理,可隐藏IP 爬虫IP代理池 爬虫IP代理池 云打码 采用全球领先的秒传识别系统,50%图片零秒识别,人工平均处理时间0-3秒 超级鹰 专业的验证码云端识别服务,让验证码识别更快速、更准确、更强大 八爪鱼采集器 一款使用简单、功能强大的网络爬虫工具,完全可视化操作,无需编写代码,内置海量模板,支持任意网络数据抓取 Python 逆向 Python 逆向相关资源 Python 爬虫集合 Python 爬虫集合 Python 入门网络爬虫之精华版 Python 入门网络爬虫之精华版 爬虫项目进阶实战 Python3 爬虫项目进阶实战、JS加解密、逆向教程、css 加密、字体加密 Python 模拟登陆一些大型网站 Python 模拟登陆一些大型网站 系统化学习 Python 爬虫 系统化学习 Python 爬虫 Python3 网络爬虫实战 Python3 网络爬虫实战 在线视频 腾讯课堂 腾讯推出的专业在线教育平台,聚合大量优质教育机构和名师 网易云课堂 网易旗下一个专注职业技能提升的在线学习平台。立足于实用性的要求,与多家教育培训机构和行业的专家、讲师建立合作 中国大学 MOOC 中国大学MOOC(慕课),国家精品课程在线学习平台 黑马程序员 致力于培养中级程序员,是业内以口碑闻名的IT教育培训机构 课工场 更可靠的IT就业教育平台,针对大学生量身定制人工智能、大数据、云计算、区块链、Java大数据开发等大学生IT培训课程 极客学院 极客学院作为中国专业IT职业在线教育平台,拥有海量高清IT职业课程,涵盖30+个技术领域 慕课网 慕课网(IMOOC)是IT技能学习平台。慕课网(IMOOC)提供了丰富的移动端开发、php开发、web前端、android开发以及html5等视频教程资源公开课 尚硅谷 尚硅谷Java培训,谷粉与老学员为你推荐的Java培训、Web前端培训、前端培训、大数据培训、Python培训;0基础入学,学员就业起薪屡创新高! 实验楼 国内领先的IT在线编程及在线实训学习平台,专业导师提供精选的实践项目,创新的技术使得学习者无需配置繁琐的本地环境,随时在线流畅使用 优达学城 Udacity是来自硅谷的前沿技术平台,为广大学子提供WEB前端开发、Python/JAVA编程、IOS/Android开发、人工智能开发等一系列在线课程及实战项目,满足学员灵活的学习需求 51CTO学院 51CTO学院IT职业在线教育平台是依托12年行业品牌、1400万IT技术用户建立的专业IT技能学习培训平台,已签约1000多位技术专家发布了12万个自学式实战视频教程 CSDN 学院 CSDN 学院作为IT在线教育平台,涵盖人工智能、考试认证、移动开发、大数据技术领域职业课程 老男孩IT教育 隶属北京一天天教育科技有限公司,是一直专注于Linux培训、Linux系统及架构师培训、Python培训、网络安全培训,大数据实战的高端培训机构 千锋教育 千锋教育 - 坚持教育初心,坚持面授品质,IT培训良心品牌 博客论坛 鱼C工作室 鱼C工作室-免费编程视频教学|Python教学|Web开发教学|全栈开发教学|C语言教学|汇编教学|Win32开发|加密与解密|Linux教学 吾爱破解 致力于软件安全与病毒分析的前沿,丰富的技术版块交相辉映,由无数热衷于软件加密解密及反病毒爱好者共同维护 廖雪峰 廖雪峰的官方网站 - 研究互联网产品和技术,提供原创中文精品教程 崔庆才 崔庆才的个人博客,专注PHP,Python,爬虫,深度学习,机器学习,数据分析 莫烦Python 专注Python、机器学习、深度学习 唐松 专注Python网络爬虫, 数据科学, 数据挖掘, 数据分析 阮一峰 上海财经大学世界经济博士研究生,计算机科普博主,对自由软件有着坚定不移的信念 学习平台 菜鸟教程 提供了编程的基础技术教程, 介绍了HTML、CSS、Javascript、Python,Java,Ruby,C,PHP , MySQL等各种编程语言的基础知识 W3school 领先的 Web 技术教程 C语言网 C语言网 - 领先实用的编程在线学习网站 前端网 前端网,最好的自学web前端网站 牛客网 牛客网 - 互联网求职神器和备考学习平台 How2J How2J的Java教程, 内容涵盖J2SE、WEB前端、J2EE、框架技术等全面的Java内容 站长工具 新浪短网址 多种后缀短网址生成 百度短网址 百度旗下专业的网址缩短服务 站长工具 - 站长之家 站长工具,SEO工具,权重查询,收录查询,PR查询,ICP备案查询,whois查询,友情链接查询,反向链接查询,网站测试,IP查询,Alexa查询 阿里云 whois 查询 whois查询,域名whois,域名注册信息,whois查询工具,whois信息,域名信息 NnameBeta 国际域名搜索、域名注册、国别域名注册、域名比价 Domcomp 域名比价,Domain Name Price and Availability. 仿站工具箱 在线仿站工具箱 超级 SEO 外链工具 网站自动化宣传机器/免费的超级外链工具可批量增加外链 百度站长平台 百度搜索资源平台 - 让网站更具价值 搜狗站长平台 搜狗站长平台 - 全面掌握在搜狗搜索中的数据表现 360 站长平台 360 站长平台 - 给网站带来更多流量和展现 Google 站长平台 Google 网站站长 - 支持、学习、互动交流和 Search Console – Google Bing 网站管理员工具 Bing 网站管理员工具 百度广告联盟 百度广告联盟为您的流量增值 Google AdSense Google 广告平台 百度统计 百度统计 — 最大的中文网站分析平台 友盟+ 国内领先的第三方全域数据智能服务商 ICP/IP地址/域名信息备案管理系统 工业和信息化部ICP/IP地址/域名信息备案管理系统 全国互联网安全管理服务平台 公安备案网 - 全国互联网安全管理服务平台 IT工具箱 在线工具 - 程序员的工具箱 站长工具、代码格式化、压缩、加密、解密、下载链接转换等 在线工具 - OSCHINA.NET社区 常用文档、常用对照表、代码处理、Html/Js/Css工具、加密/转码工具等 记磊工具箱 Dns检测、CSS格式化、超级Ping、端口扫描等 孟坤工具箱 css一键美化、文本差异比较、代码高亮等 Syntax Highlight Syntax Highlight Code In Word Documents,在Word文档中插入漂亮的代码 Text to ASCII Art Generator Text to ASCII Art Generator,字符串转成 ASCII 码图案 MDEditor 开源在线 Markdown 编辑器 临时邮箱 匿名注册不常用的网站/论坛,保护隐私免骚扰 SM.MS SM 免费图床,每个文件最大支持 5MB 路过图床 免费公共图床,支持最大10MB、批量上传 Greasy Fork 安全、实用的用户脚本大全 Hello World 大全 收集了大约481种 Hello World 程序,涵盖了目前已知的所有编程语言,另加上 67 人类语言 动画展示各种路径搜索算法 动画展示各种路径搜索算法 IT eBooks 可以下载IT电子书籍的网站(英文) GEEKTyper 在线模拟黑客工作的虚拟桌面系统,提供多种黑客工作的场景 免费计算机编程类中文书籍 免费计算机编程类中文书籍 EaseUS Partition Master 磁盘分区管理软件,不用重装系统,就可以重新划分磁盘空间 文件处理 Convertio 在线文件转换工具,支持超过309种不同的文档、图像、电子表格、电子书、文档、演示文稿、音频和视频格式 Office-Converter 免费在线转换视频,在线音频转换,在线图形转换,在线文档转换和在线压缩格式 TinyPNG PNG/JPG图片在线压缩利器 Squoosh Google开源在线压缩、调整工具,支持WebP ILoveIMG 永远免费的在线图片处理工具,可在线编辑,压缩、裁剪、转换、水印等 Smallpdf Smallpdf - A Free Solution to all your PDF Problems,PDF压缩、转换、分割、合并等 PHOTOMOSH 故障艺术在线生成,可以输出jpg、gif和视频 稿定抠图 免费在线抠图软件,图片快速换背景-抠白底图 U钙网 完全免费的LOGO在线设计制作工具 SVGOMG SVG在线压缩平台 在线图片透明圆角处理 在线图片透明圆角处理 草料二维码 国内创建二维码在线应用 Logaster 在线免费创建简单logo及名片设计 Preloaders Loading 懒加载动画在线制作 Loading 制作GIF、SVG、CSS加载动画图标 waifu2x 图片智能无损放大2倍,适合动漫、插画等 智图 腾讯ISUX前端团队开发的一个专门用于图片压缩和图片格式转换的平台 音乐免费下载 全网音乐免费下载工具 OK资源采集 OK资源采集-最新影视资源大全 网易见外工作台 针对视频、图片、文档、音频都可以进行翻译转写操作,每天两小时免费使用 HiPDF 一站式解决所有PDF相关的问题 视频鱼 在线下载各大网站视频的网站 ScreenToGif 开源、轻量级却非常强大的录屏软件,快速将屏幕录制成高清GIF 设计素材 Iconfont 阿里巴巴矢量图标库,提供矢量图标下载、在线存储、格式转换等功能 Font Awesome 一个基于CSS 和 LESS 的字体和图标工具包 Flaticon 海量扁平化免费的图标库 icons8 独特系统平台风格和web图标库,下载免费图标,音乐 千图网 海量原创设计模板免费下载 昵图网 国内海量平面免费素材下载 千库网 免费 png 图片背景素材下载 Pexels 才华横溢的摄影作者在这里免费分享最精彩的素材照片和视频 必应壁纸 必应每日高清壁纸 Piqsels 精美的免版税图库 私藏字体 优质字体免费下载站 第一 PPT 网 免费 PPT 模板下载 吾道幻灯片 全新的office生产力工具,支持演示文稿、PPT模板、协同办公,可以帮助用户轻松创建具有视觉吸引力的幻灯片 Mixkit 免费、高质量、可商用的视频素材分享网站 The Stocks 对各大图片网站进行整合,免费优质图片下载 极简壁纸 高质量精品壁纸网站 NASA Image and Video Library 美国国家航天局的官方库,从此太空类的素材再也不是问题 Unsplash 质量超高的免费图片素材库,无需注册,直接下载 WordArt 文字云工具 效率软件 分流抢票 全程自动抢票,自动抢候补,自动识别验证码,多线程秒单、稳定捡漏,支持多天、多车次、多席别、多乘客等功能 PanDownload 百度网盘下载神器 Quicker 为常用操作建立捷径,PC 快捷动作面板,让效率触手可及! 万彩办公大师 免费、轻松处理文档/音视频/图片的工具 LICEcap 简洁易用的动画屏幕录制软件,它可将屏幕录像的内容直接保存为高质量(每帧颜色数量可超过256)GIF动态图片格式 Snipaste 简单但强大的截图工具,支持截图 + 贴图 FSCapture 一个强大的,轻量级的,功能齐全的屏幕捕获工具 Everything 速度最快的的文件搜索工具 DeskPins 顶置任意窗口 TrafficMonitor 一个用于显示当前网速、CPU及内存利用率的桌面悬浮窗软件 PicGo 由 electronic-vue 构建的简单而精美的图片上传工具 PowerToys 微软为 Windows 系统推出的一系列免费实用小工具合集 Dism++ 一款根据微软底层的架构结构设计的一个系统维护工具,全球第一款基于 CBS 的 Dism GUI 实现 ColorPix 屏幕取色小工具 CCleaner 一款免费的系统优化和隐私保护工具 GifCam 集录制与剪辑为一体的屏幕 GIF 动画制作工具,录制后的动画可以逐帧编辑 EV录屏 一款免费并且不添加水印的录屏工具 Fliqlo 一款极简主义的时钟屏保软件 Fences 栅栏管理桌面,使桌面更加整洁有条理 Q-dir 多窗口文件整理工具 WGestures 鼠标手势工具 XMind 一个全功能的思维导图和头脑风暴软件 速盘 免登录,自动查询提取码,极速的度盘下载工具 f.lux 国外开源的护眼软件,通过根据时间调节屏幕颜色,减少蓝光对视力的影响 云服务商 阿里云 阿里云 - 为了无法计算的价值 腾讯云 腾讯云 - 产业智变 云启未来 百度云 百度云 - 计算无限可能 华为云 华为云 - +智能,见未来 京东云 京东云 - 遇见无限可能 西部数码 西部数码 - 云服务器、虚拟主机、域名注册17年知名云计算服务提供商! 景安云 景安云 - 专业的数据中心服务商 七牛云 七牛云 - 国内领先的企业级云服务商 又拍云 又拍云 - 加速在线业务-CDN-云存储 美橙互联 美橙互联 - 域名注册、企业建站、云服务器、企业网络推广整体解决方案服务商! UCloud UCloud - 中立 安全 可信赖的云计算服务商 AWS AWS 云服务 - 专业的大数据和云计算服务以及云解决方案提供商 Microsoft Azure Azure. Invent with purpose. GoDaddy GoDaddy - 提供域名注册和互联网主机服务的美国公司 Cloudflare Cloudflare - 网络性能和安全公司 jsDelivr jsDelivr - A free, fast, and reliable Open Source CDN for npm and GitHub 众包平台 猿急送 专注于 IT 众包领域,职位内容大多集中于 UI 设计、产品设计、程序开发、产品运营等需求 开源众包 开源中国旗下外包网站,项目大多是团队的整包项目,适合多人组团接单 外包大师 PMCAFF旗下的一个众包开发平台,目前以技术开发为主,以众包开发和自有开发相结合形式运营 人人开发 集可视化开发,应用市场,威客众包,PaaS云于一体的企业级应用服务平台 快码 提供智能硬件、各种智能共享项目解决方案,为互联网创业者提供APP、小程序、公众号开发。 我爱方案网 专注于硬件类外包,电子方案开发供应链众包平台,软件外包,方案,硬件开发方案,硬件设计开发 英选 提供可信赖的定制开发外包服务,包括企业品牌官网、电商系统及创新定制产品开发 智筹 为企业&创业者提供互联网高级人才直租服务。按次直租,解决临时、突发问题;按月直租,建立长期兼职合作;按任务直租,解决有明确预算的外包任务 开发邦 互联网软件定制开发与软件外包开发服务,十年互联网软件定制开发经验 码市 Coding 推出的互联网软件外包服务平台,意在连接需求方与广大开发者。让项目的需求方快速的找到合适的开发者,完成项目开发工作 自由职客 自由职客是权威的IT互联网行业灵活用工交易平台,外包,众包,兼职,招聘,erp,sap 解放号 解放号众包平台提供软件开发外包、人力驻场服务等软件项目外包服务。解放号的软件项目交付全流程可视化监控与全生命周期管理能力 程序员客栈 领先的程序员自由工作平台,38万+优秀开发者,您的专属云端开发团队,BAT级别的开发者,标准化的服务和交付 码易 智网易联旗下IT软件服务平台,集软件商城、企业应用、电商软件、crm软件、商务服务平台于一体的一站式软件外包开发服务平台 电鸭社区 电鸭社区旨在推动自由工作方式在国内渐进式发展,区别于传统方式的工作职位,倡导「只工作,不上班」的工作心态 Sxsoft 中国最早的外包服务平台,18年口碑服务,20万程序员、100+专业软件开发公司,专注解决各类软件开发需求 实现网 为企业提供BAT等名企背景的、靠谱的开发设计兼职人才和自由职业者,满足企业项目外包、驻场开发、远程兼职、技术咨询等短期人力需求 智城外包网 零佣金开发资源平台,认证担保,全程无忧,专业的软件外包网和项目外包、项目开发、人力外派、短期招聘、人力资源交易平台 站内游戏 2048 网页版 2048 小游戏 圈小猫 点击圆点围住小猫 3D 元素周期表 3D 网页展示元素周期表 五子棋 网页版简易五子棋 吃豆人 躲避怪物,吃掉豆子 网页钢琴 简易网页版钢琴 更多导航 创造狮导航 创造狮,一个创意工作者的导航,专注分享正版优质设计、前端、产品、运营的书签导航,设计教程、设计规范、颜色搭配、灵感创意、前端框架、开发者工具、互联网新品推荐、运营数据分析、自媒体和工具利器好用的分类导航大全 大数据导航 大数据导航,以大数据产业为主,大数据工具为辅,给用户提供一个更加快速找到大数据相关的工具平台 优设导航 优设网站导航为设计师提供ps教程、UI设计、素材下载、高清图库、配色方案、用户体验、网页设计等全方位设计师网站导航指引 牛导航 实用工具导航 聚BT 聚BT - 聚合最优质的BT、磁力资源 ShareHub ShareHub - 资源和工具的集合 狼牌工作网址导航 工具,资源,方法,All IN ONE的办公工作网址导航 COPYRIGHT 2018 - 2020 WEBSTACK 丨 DESIGNED BY VIGGO 丨 CHANGED BY TRHX $(document).ready(function() { $(document).on('click', '.has-sub', function(){ var _this = $(this) if(!$(this).hasClass('expanded')) { setTimeout(function(){ _this.find('ul').attr(\"style\",\"\") }, 300); } else { $('.has-sub ul').each(function(id,ele){ var _that = $(this) if(_this.find('ul')[0] != ele) { setTimeout(function(){ _that.attr(\"style\",\"\") }, 300); } }) } }) $('.user-info-menu .hidden-sm').click(function(){ if($('.sidebar-menu').hasClass('collapsed')) { $('.has-sub.expanded > ul').attr(\"style\",\"\") } else { $('.has-sub.expanded > ul').show() } }) $(\"#main-menu li ul li\").click(function() { $(this).siblings('li').removeClass('active'); // 删除其他兄弟元素的样式 $(this).addClass('active'); // 添加当前元素的样式 }); $(\"a.smooth\").click(function(ev) { ev.preventDefault(); public_vars.$mainMenu.add(public_vars.$sidebarProfile).toggleClass('mobile-is-visible'); ps_destroy(); $(\"html, body\").animate({ scrollTop: $($(this).attr(\"href\")).offset().top - 30 }, { duration: 500, easing: \"swing\" }); }); return false; }); var href = \"\"; var pos = 0; $(\"a.smooth\").click(function(e) { $(\"#main-menu li\").each(function() { $(this).removeClass(\"active\"); }); $(this).parent(\"li\").addClass(\"active\"); e.preventDefault(); href = $(this).attr(\"href\"); pos = $(href).position().top - 30; });"}],"posts":[{"title":"前程无忧招聘信息爬取 + 数据可视化","slug":"A90-pyspider-51job","date":"2020-07-13T13:07:35.909Z","updated":"2020-08-06T03:35:57.290Z","comments":true,"path":"2020/07/13/A90-pyspider-51job/","link":"","permalink":"https://www.itrhx.com/2020/07/13/A90-pyspider-51job/","excerpt":"","text":"爬取时间:2020-07-11 实现目标:根据用户输入的关键字爬取相关职位信息存入 MongoDB,读取数据进行可视化展示。 涉及知识:请求库 requests、Xpath 语法、数据库 MongoDB、数据处理与可视化 Numpy、Pandas、Matplotlib。 完整代码:https://github.com/TRHX/Python3-Spider-Practice/tree/master/51job 其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice 爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278 【1x00】获取数据 get_51job_data.py【01x01】构建请求地址以 Python 职位为例,请求地址如下: 第一页:https://search.51job.com/list/000000,000000,0000,00,9,99,python,2,1.html 第二页:https://search.51job.com/list/000000,000000,0000,00,9,99,python,2,2.html 第三页:https://search.51job.com/list/000000,000000,0000,00,9,99,python,2,3.html 初始化函数: 1234def __init__(self): self.base_url = 'https://search.51job.com/list/000000,000000,0000,00,9,99,%s,2,%s.html' self.headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.13 Safari/537.36'} self.keyword = input('请输入关键字:') 【01x02】获取总页数在页面的下方给出了该职位一共有多少页,使用 Xpath 和正则表达式提取里面的数字,方便后面翻页爬取使用,注意页面编码为 gbk。 12345678910def tatal_url(self): url = self.base_url % (self.keyword, str(1)) response = requests.get(url=url, headers=self.headers) tree = etree.HTML(response.content.decode('gbk')) # 提取一共有多少页 text = tree.xpath(\"//div[@class='p_in']/span[1]/text()\")[0] number = re.findall('[0-9]', text) number = int(''.join(number)) print('%s职位共有%d页' % (self.keyword, number)) return number 【01x03】提取详情页 URL定义一个 detail_url() 方法,传入总页数,循环提取每一页职位详情页的 URL,将每一个详情页 URL 传递给 parse_data() 方法,用于解析详情页内的具体职位信息。 提取详情页时有以下几种特殊情况: 特殊情况一:如果有前程无忧自己公司的职位招聘信息掺杂在里面,他的详情页结构和普通的不一样,页面编码也有差别。 页面示例:https://51rz.51job.com/job.html?jobid=115980776 页面真实数据请求地址类似于:https://coapi.51job.com/job_detail.php?jsoncallback=&key=&sign=params={"jobid":""} 请求地址中的各参数值通过 js 加密:https://js.51jobcdn.com/in/js/2018/coapi/coapi.min.js 特殊情况二:部分公司有自己的专属页面,此类页面的结构也不同于普通页面 页面示例:http://dali.51ideal.com/jobdetail.html?jobid=121746338 为了规范化,本次爬取将去掉这部分特殊页面,仅爬取 URL 带有 jobs.51job.com 的数据 123456789101112131415161718192021def detail_url(self, number): for num in range(1, number+1): url = self.base_url % (self.keyword, str(num)) response = requests.get(url=url, headers=self.headers) tree = etree.HTML(response.content.decode('gbk')) detail_url1 = tree.xpath(\"//div[@class='dw_table']/div[@class='el']/p/span/a/@href\") \"\"\" 深拷贝一个 url 列表,如果有连续的不满足要求的链接,若直接在原列表里面删除, 则会漏掉一些链接,因为每次删除后的索引已改变,因此在原列表中提取不符合元素 后,在深拷贝的列表里面进行删除。最后深拷贝的列表里面的元素均符合要求。 \"\"\" detail_url2 = copy.deepcopy(detail_url1) for url in detail_url1: if 'jobs.51job.com' not in url: detail_url2.remove(url) self.parse_data(detail_url2) print('第%d页数据爬取完毕!' % num) time.sleep(2) print('所有数据爬取完毕!') 【01x04】提取职位信息解析详情页时页面编码是 gbk,但是某些页面在解析时仍然会报编码错误,因此使用 try-except 语句捕捉编码错误(UnicodeDecodeError),如果该页面有编码错误则直接 return 结束函数。 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364def parse_data(self, urls): \"\"\" position: 职位 wages: 工资 region: 地区 experience: 经验 education: 学历 need_people: 招聘人数 publish_date: 发布时间 english: 英语要求 welfare_tags: 福利标签 job_information: 职位信息 work_address: 上班地址 company_name: 公司名称 company_nature: 公司性质 company_scale: 公司规模 company_industry: 公司行业 company_information: 公司信息 \"\"\" for url in urls: response = requests.get(url=url, headers=self.headers) try: text = response.content.decode('gbk') except UnicodeDecodeError: return tree = etree.HTML(text) \"\"\" 提取内容时使用 join 方法将列表转为字符串,而不是直接使用索引取值, 这样做的好处是遇到某些没有的信息直接留空而不会报错 \"\"\" position = ''.join(tree.xpath(\"//div[@class='cn']/h1/text()\")) wages = ''.join(tree.xpath(\"//div[@class='cn']/strong/text()\")) # 经验、学历、招聘人数、发布时间等信息都在一个标签里面,逐一使用列表解析式提取 content = tree.xpath(\"//div[@class='cn']/p[2]/text()\") content = [i.strip() for i in content] if content: region = content[0] else: region = '' experience = ''.join([i for i in content if '经验' in i]) education = ''.join([i for i in content if i in '本科大专应届生在校生硕士']) need_people = ''.join([i for i in content if '招' in i]) publish_date = ''.join([i for i in content if '发布' in i]) english = ''.join([i for i in content if '英语' in i]) welfare_tags = ','.join(tree.xpath(\"//div[@class='jtag']/div//text()\")[1:-2]) job_information = ''.join(tree.xpath(\"//div[@class='bmsg job_msg inbox']/p//text()\")).replace(' ', '') work_address = ''.join(tree.xpath(\"//div[@class='bmsg inbox']/p//text()\")) company_name = ''.join(tree.xpath(\"//div[@class='tCompany_sidebar']/div[1]/div[1]/a/p/text()\")) company_nature = ''.join(tree.xpath(\"//div[@class='tCompany_sidebar']/div[1]/div[2]/p[1]//text()\")) company_scale = ''.join(tree.xpath(\"//div[@class='tCompany_sidebar']/div[1]/div[2]/p[2]//text()\")) company_industry = ''.join(tree.xpath(\"//div[@class='tCompany_sidebar']/div[1]/div[2]/p[3]/@title\")) company_information = ''.join(tree.xpath(\"//div[@class='tmsg inbox']/text()\")) job_data = [position, wages, region, experience, education, need_people, publish_date, english, welfare_tags, job_information, work_address, company_name, company_nature, company_scale, company_industry, company_information] save_mongodb(job_data) 【01x05】保存数据到 MongoDB指定一个名为 job51_spider 的数据库和一个名为 data 的集合,依次将信息保存至 MongoDB。 1234567891011121314151617181920212223def save_mongodb(data): client = pymongo.MongoClient(host='localhost', port=27017) db = client.job51_spider collection = db.data save_data = { '职位': data[0], '工资': data[1], '地区': data[2], '经验': data[3], '学历': data[4], '招聘人数': data[5], '发布时间': data[6], '英语要求': data[7], '福利标签': data[8], '职位信息': data[9], '上班地址': data[10], '公司名称': data[11], '公司性质': data[12], '公司规模': data[13], '公司行业': data[14], '公司信息': data[15] } collection.insert_one(save_data) 【2x00】数据可视化 draw_bar_chart.py【02x01】数据初处理从 MongoDB 里面读取数据为 DataFrame 对象,本次可视化只分析工资与经验、学历的关系,所以只取这三项,由于获取的数据有些是空白值,因此使用 replace 方法将空白值替换成缺失值(NaN),然后使用 DataFrame 对象的 dropna() 方法删除带有缺失值(NaN)的行。将工资使用 apply 方法,将每个值应用于 wish_data 方法,即对每个值进行清洗。 1234567891011121314151617def processing_data(): # 连接数据库,从数据库读取数据(也可以导出后从文件中读取) client = pymongo.MongoClient(host='localhost', port=27017) db = client.job51_spider collection = db.data # 读取数据并转换为 DataFrame 对象 data = pd.DataFrame(list(collection.find())) data = data[['工资', '经验', '学历']] # 使用正则表达式选择空白的字段并填充为缺失值,然后删除带有缺失值的所有行 data.replace(to_replace=r'^\\s*$', value=np.nan, regex=True, inplace=True) data = data.dropna() # 对工资数据进行清洗,处理后的工作单位:元/月 data['工资'] = data['工资'].apply(wish_data) return data 【02x02】数据清洗12345678910111213141516171819202122232425262728293031323334353637383940414243def wish_data(wages_old): \"\"\" 数据清洗规则: 分为元/天,千(以上/下)/月,万(以上/下)/月,万(以上/下)/年 若数据是一个区间的,则求其平均值,最后的值统一单位为元/月 \"\"\" if '元/天' in wages_old: if '-' in wages_old.split('元')[0]: wages1 = wages_old.split('元')[0].split('-')[0] wages2 = wages_old.split('元')[0].split('-')[1] wages_new = (float(wages2) + float(wages1)) / 2 * 30 else: wages_new = float(wages_old.split('元')[0]) * 30 return wages_new elif '千/月' in wages_old or '千以下/月' in wages_old or '千以上/月' in wages_old: if '-' in wages_old.split('千')[0]: wages1 = wages_old.split('千')[0].split('-')[0] wages2 = wages_old.split('千')[0].split('-')[1] wages_new = (float(wages2) + float(wages1)) / 2 * 1000 else: wages_new = float(wages_old.split('千')[0]) * 1000 return wages_new elif '万/月' in wages_old or '万以下/月' in wages_old or '万以上/月' in wages_old: if '-' in wages_old.split('万')[0]: wages1 = wages_old.split('万')[0].split('-')[0] wages2 = wages_old.split('万')[0].split('-')[1] wages_new = (float(wages2) + float(wages1)) / 2 * 10000 else: wages_new = float(wages_old.split('万')[0]) * 10000 return wages_new elif '万/年' in wages_old or '万以下/年' in wages_old or '万以上/年' in wages_old: if '-' in wages_old.split('万')[0]: wages1 = wages_old.split('万')[0].split('-')[0] wages2 = wages_old.split('万')[0].split('-')[1] wages_new = (float(wages2) + float(wages1)) / 2 * 10000 / 12 else: wages_new = float(wages_old.split('万')[0]) * 10000 / 12 return wages_new 【02x03】绘制经验与平均薪资关系图1234567891011121314151617181920212223242526def wages_experience_chart(data): # 根据经验分类,求不同经验对应的平均薪资 wages_experience = data.groupby('经验').mean() # 获取经验和薪资的值,将其作为画图的 x 和 y 数据 w = wages_experience['工资'].index.values e = wages_experience['工资'].values # 按照经验对数据重新进行排序,薪资转为 int 类型(也可以直接在前面对 DataFrame 按照薪资大小排序) wages = [w[6], w[1], w[2], w[3], w[4], w[5], w[0]] experience = [int(e[6]), int(e[1]), int(e[2]), int(e[3]), int(e[4]), int(e[5]), int(e[0])] # 绘制柱状图 plt.rcParams['font.sans-serif'] = ['Microsoft YaHei'] plt.figure(figsize=(9, 6)) x = wages y = experience color = ['#E41A1C', '#377EB8', '#4DAF4A', '#984EA3', '#FF7F00', '#FFFF33', '#A65628'] plt.bar(x, y, color=color) for a, b in zip(x, y): plt.text(a, b, b, ha='center', va='bottom') plt.title('Python 相关职位经验与平均薪资关系', fontsize=13) plt.xlabel('经验', fontsize=13) plt.ylabel('平均薪资(元 / 月)', fontsize=13) plt.savefig('wages_experience_chart.png') plt.show() 【02x04】绘制学历与平均薪资关系图12345678910111213141516171819202122def wages_education_chart(data): # 根据学历分类,求不同学历对应的平均薪资 wages_education = data.groupby('学历').mean() # 获取学历和薪资的值,将其作为画图的 x 和 y 数据 wages = wages_education['工资'].index.values education = [int(i) for i in wages_education['工资'].values] # 绘制柱状图 plt.rcParams['font.sans-serif'] = ['Microsoft YaHei'] plt.figure(figsize=(9, 6)) x = wages y = education color = ['#E41A1C', '#377EB8', '#4DAF4A'] plt.bar(x, y, color=color) for a, b in zip(x, y): plt.text(a, b, b, ha='center', va='bottom') plt.title('Python 相关职位学历与平均薪资关系', fontsize=13) plt.xlabel('学历', fontsize=13) plt.ylabel('平均薪资(元 / 月)', fontsize=13) plt.savefig('wages_education_chart.png') plt.show() 【3x00】数据截图一共有 34009 条数据,完整数据已放在 github,可自行下载。 MongoDB: CSV 文件: JSON 文件: 关系图: 【4x00】完整代码完整代码地址(点亮 star 有 buff 加成):https://github.com/TRHX/Python3-Spider-Practice/tree/master/51job 其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice 爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫实战","slug":"Python3-学习笔记/爬虫实战","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫实战/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"前程无忧","slug":"前程无忧","permalink":"https://www.itrhx.com/tags/前程无忧/"},{"name":"数据可视化","slug":"数据可视化","permalink":"https://www.itrhx.com/tags/数据可视化/"}]},{"title":"COVID-19 肺炎疫情数据实时监控(Python 爬虫 + Pyecharts 数据可视化 + Wordcloud 词云图)","slug":"A89-COVID-19","date":"2020-07-06T04:25:20.285Z","updated":"2020-08-06T03:34:40.274Z","comments":true,"path":"2020/07/06/A89-COVID-19/","link":"","permalink":"https://www.itrhx.com/2020/07/06/A89-COVID-19/","excerpt":"","text":"12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/107140534未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【1x00】前言本来两三个月之前就想搞个疫情数据实时数据展示的,由于各种不可抗拒因素一而再再而三的鸽了,最近终于抽空写了一个,数据是用 Python 爬取的百度疫情实时大数据报告,请求库用的 requests,解析用的 Xpath 语法,词云用的 wordcloud 库,数据可视化用 pyecharts 绘制的地图和折线图,数据储存在 Excel 表格里面,使用 openpyxl 对表格进行处理。 本程序实现了累计确诊地图展示和每日数据变化折线图展示,其他更多数据的获取和展示均可在程序中进行拓展,可以将程序部署在服务器上,设置定时运行,即可实时展示数据,pyecharts 绘图模块也可以整合到 Web 框架(Django、Flask等)中使用。 在获取数据时有全球和境外两个概念,全球包含中国,境外不包含中国,后期绘制的四个图:中国累计确诊地图、全球累计确诊地图(包含中国)、中国每日数据折线图、境外每日数据折线图(不包含中国)。 注意项:直接向该网页发送请求获取的响应中,没有每个国家的每日数据,该数据获取的地址是:https://voice.baidu.com/newpneumonia/get?target=trend&isCaseIn=1&stage=publish 预览地址:http://cov.itrhx.com/ 数据来源:https://voice.baidu.com/act/newpneumonia/newpneumonia/ pyecharts 文档:https://pyecharts.org/ openpyxl 文档:https://openpyxl.readthedocs.io/ wordcloud 文档:http://amueller.github.io/word_cloud/ 【2x00】思维导图 【3x00】数据结构分析通过查看百度的疫情数据页面,可以看到很多整齐的数据,猜测就是疫情相关的数据,保存该页面,对其进行格式化,很容易可以分析出所有的数据都在 <script type="application/json" id="captain-config"></script> 里面,其中 title 里面是一些 Unicode 编码,将其转为中文后更容易得到不同的分类数据。 由于数据繁多,可以将数据主体部分提取出来,删除一些重复项和其他杂项,留下数据大体位置并分析数据结构,便于后期的数据提取,经过处理后的数据大致结构如下: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230<script type=\"application/json\" id=\"captain-config\"> { \"component\": [ { \"mapLastUpdatedTime\": \"2020.07.05 16:13\", // 国内疫情数据最后更新时间 \"caseList\": [ // caseList 列表,每一个元素是一个字典 { \"confirmed\": \"1\", // 每个字典包含中国每个省的每一项疫情数据 \"died\": \"0\", \"crued\": \"1\", \"relativeTime\": \"1593792000\", \"confirmedRelative\": \"0\", \"diedRelative\": \"0\", \"curedRelative\": \"0\", \"curConfirm\": \"0\", \"curConfirmRelative\": \"0\", \"icuDisable\": \"1\", \"area\": \"西藏\", \"subList\": [ // subList 列表,每一个元素是一个字典 { \"city\": \"拉萨\", // 每个字典包含该省份对应的每个城市疫情数据 \"confirmed\": \"1\", \"died\": \"0\", \"crued\": \"1\", \"confirmedRelative\": \"0\", \"curConfirm\": \"0\", \"cityCode\": \"100\" } ] } ], \"caseOutsideList\": [ // caseOutsideList 列表,每一个元素是一个字典 { \"confirmed\": \"241419\", // 每个字典包含各国的每一项疫情数据 \"died\": \"34854\", \"crued\": \"191944\", \"relativeTime\": \"1593792000\", \"confirmedRelative\": \"223\", \"curConfirm\": \"14621\", \"icuDisable\": \"1\", \"area\": \"意大利\", \"subList\": [ // subList 列表,每一个元素是一个字典 { \"city\": \"伦巴第\", // 每个字典包含每个国家对应的每个城市疫情数据 \"confirmed\": \"94318\", \"died\": \"16691\", \"crued\": \"68201\", \"curConfirm\": \"9426\" } ] } ], \"summaryDataIn\": { // summaryDataIn 国内总的疫情数据 \"confirmed\": \"85307\", \"died\": \"4648\", \"cured\": \"80144\", \"asymptomatic\": \"99\", \"asymptomaticRelative\": \"7\", \"unconfirmed\": \"7\", \"relativeTime\": \"1593792000\", \"confirmedRelative\": \"19\", \"unconfirmedRelative\": \"1\", \"curedRelative\": \"27\", \"diedRelative\": \"0\", \"icu\": \"6\", \"icuRelative\": \"0\", \"overseasInput\": \"1931\", \"unOverseasInputCumulative\": \"83375\", \"overseasInputRelative\": \"6\", \"unOverseasInputNewAdd\": \"13\", \"curConfirm\": \"515\", \"curConfirmRelative\": \"-8\", \"icuDisable\": \"1\" }, \"summaryDataOut\": { // summaryDataOut 国外总的疫情数据 \"confirmed\": \"11302569\", \"died\": \"528977\", \"curConfirm\": \"4410601\", \"cured\": \"6362991\", \"confirmedRelative\": \"206165\", \"curedRelative\": \"190018\", \"diedRelative\": \"4876\", \"curConfirmRelative\": \"11271\", \"relativeTime\": \"1593792000\" }, \"trend\": { // trend 字典,包含国内每日的疫情数据 \"updateDate\": [], // 日期 \"list\": [ // list 列表,每项数据及其对应的值 { \"name\": \"确诊\", \"data\": [] }, { \"name\": \"疑似\", \"data\": [] }, { \"name\": \"治愈\", \"data\": [] }, { \"name\": \"死亡\", \"data\": [] }, { \"name\": \"新增确诊\", \"data\": [] }, { \"name\": \"新增疑似\", \"data\": [] }, { \"name\": \"新增治愈\", \"data\": [] }, { \"name\": \"新增死亡\", \"data\": [] }, { \"name\": \"累计境外输入\", \"data\": [] }, { \"name\": \"新增境外输入\", \"data\": [] } ] }, \"foreignLastUpdatedTime\": \"2020.07.05 16:13\", // 国外疫情数据最后更新时间 \"globalList\": [ // globalList 列表,每一个元素是一个字典 { \"area\": \"亚洲\", // 按照不同洲进行分类 \"subList\": [ // subList 列表,每个洲各个国家的疫情数据 { \"died\": \"52\", \"confirmed\": \"6159\", \"crued\": \"4809\", \"curConfirm\": \"1298\", \"confirmedRelative\": \"0\", \"relativeTime\": \"1593792000\", \"country\": \"塔吉克斯坦\" } ], \"died\": \"56556\", // 每个洲总的疫情数据 \"crued\": \"1625562\", \"confirmed\": \"2447873\", \"curConfirm\": \"765755\", \"confirmedRelative\": \"60574\" }, { \"area\": \"其他\", // 其他特殊区域疫情数据 \"subList\": [ { \"died\": \"13\", \"confirmed\": \"712\", \"crued\": \"651\", \"curConfirm\": \"48\", \"confirmedRelative\": \"0\", \"relativeTime\": \"1593792000\", \"country\": \"钻石公主号邮轮\" } ], \"died\": \"13\", // 其他特殊区域疫情总的数据 \"crued\": \"651\", \"confirmed\": \"712\", \"curConfirm\": \"48\", \"confirmedRelative\": \"0\" }, { \"area\": \"热门\", // 热门国家疫情数据 \"subList\": [ { \"died\": \"5206\", \"confirmed\": \"204610\", \"crued\": \"179492\", \"curConfirm\": \"19912\", \"confirmedRelative\": \"1172\", \"relativeTime\": \"1593792000\", \"country\": \"土耳其\" } ], \"died\": \"528967\", // 热门国家疫情总的数据 \"crued\": \"6362924\", \"confirmed\": \"11302357\", \"confirmedRelative\": \"216478\", \"curConfirm\": \"4410466\" }], \"allForeignTrend\": { // allForeignTrend 字典,包含国外每日的疫情数据 \"updateDate\": [], // 日期 \"list\": [ // list 列表,每项数据及其对应的值 { \"name\": \"累计确诊\", \"data\": [] }, { \"name\": \"治愈\", \"data\": [] }, { \"name\": \"死亡\", \"data\": [] }, { \"name\": \"现有确诊\", \"data\": [] }, { \"name\": \"新增确诊\", \"data\": [] } ] }, \"topAddCountry\": [ // 确诊增量最高的国家 { \"name\": \"美国\", \"value\": 53162 } ], \"topOverseasInput\": [ // 境外输入最多的省份 { \"name\": \"黑龙江\", \"value\": 386 } ] } ] }</script> 【4x00】主函数 main()分别将数据获取、词云图绘制、地图绘制写入三个文件:data_get()、data_wordcloud()、data_map(),然后使用一个主函数文件 main.py 来调用这三个文件里面的函数。 1234567891011121314import data_getimport data_wordcloudimport data_mapdata_dict = data_get.init()data_get.china_total_data(data_dict)data_get.global_total_data(data_dict)data_get.china_daily_data(data_dict)data_get.foreign_daily_data(data_dict)data_wordcloud.china_wordcloud()data_wordcloud.global_wordcloud()data_map.all_map() 【5x00】数据获取模块 data_get【5x01】初始化函数 init()使用 xpath 语法 //script[@id="captain-config"]/text() 提取里面的值,利用 json.loads 方法将其转换为字典对象,以便后续的其他函数调用。 1234567891011def init(): headers = { 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.13 Safari/537.36' } url = 'https://voice.baidu.com/act/newpneumonia/newpneumonia/' response = requests.get(url=url, headers=headers) tree = etree.HTML(response.text) dict1 = tree.xpath('//script[@id=\"captain-config\"]/text()') print(type(dict1[0])) dict2 = json.loads(dict1[0]) return dict2 【5x02】中国总数据 china_total_data()12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970def china_total_data(data): \"\"\" 1、中国省/直辖市/自治区/行政区疫情数据 省/直辖市/自治区/行政区:area 现有确诊: curConfirm 累计确诊: confirmed 累计治愈: crued 累计死亡: died 现有确诊增量: curConfirmRelative 累计确诊增量: confirmedRelative 累计治愈增量: curedRelative 累计死亡增量: diedRelative \"\"\" wb = openpyxl.Workbook() # 创建工作簿 ws_china = wb.active # 获取工作表 ws_china.title = \"中国省份疫情数据\" # 命名工作表 ws_china.append(['省/直辖市/自治区/行政区', '现有确诊', '累计确诊', '累计治愈', '累计死亡', '现有确诊增量', '累计确诊增量', '累计治愈增量', '累计死亡增量']) china = data['component'][0]['caseList'] for province in china: ws_china.append([province['area'], province['curConfirm'], province['confirmed'], province['crued'], province['died'], province['curConfirmRelative'], province['confirmedRelative'], province['curedRelative'], province['diedRelative']]) \"\"\" 2、中国城市疫情数据 城市:city 现有确诊:curConfirm 累计确诊:confirmed 累计治愈:crued 累计死亡:died 累计确诊增量:confirmedRelative \"\"\" ws_city = wb.create_sheet('中国城市疫情数据') ws_city.append(['城市', '现有确诊', '累计确诊', '累计治愈', '累计死亡', '累计确诊增量']) for province in china: for city in province['subList']: # 某些城市没有 curConfirm 数据,则将其设置为 0,crued 和 died 为空时,替换成 0 if 'curConfirm' not in city: city['curConfirm'] = '0' if city['crued'] == '': city['crued'] = '0' if city['died'] == '': city['died'] = '0' ws_city.append([city['city'], '0', city['confirmed'], city['crued'], city['died'], city['confirmedRelative']]) \"\"\" 3、中国疫情数据更新时间:mapLastUpdatedTime \"\"\" time_domestic = data['component'][0]['mapLastUpdatedTime'] ws_time = wb.create_sheet('中国疫情数据更新时间') ws_time.column_dimensions['A'].width = 22 # 调整列宽 ws_time.append(['中国疫情数据更新时间']) ws_time.append([time_domestic]) wb.save('COVID-19-China.xlsx') print('中国疫情数据已保存至 COVID-19-China.xlsx!') 【5x03】全球总数据 global_total_data()全球总数据在提取完成后,进行地图绘制时发现并没有中国的数据,因此在写入全球数据时注意要单独将中国的数据插入 Excel 中。 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364def global_total_data(data): \"\"\" 1、全球各国疫情数据 国家:country 现有确诊:curConfirm 累计确诊:confirmed 累计治愈:crued 累计死亡:died 累计确诊增量:confirmedRelative \"\"\" wb = openpyxl.Workbook() ws_global = wb.active ws_global.title = \"全球各国疫情数据\" # 按照国家保存数据 countries = data['component'][0]['caseOutsideList'] ws_global.append(['国家', '现有确诊', '累计确诊', '累计治愈', '累计死亡', '累计确诊增量']) for country in countries: ws_global.append([country['area'], country['curConfirm'], country['confirmed'], country['crued'], country['died'], country['confirmedRelative']]) # 按照洲保存数据 continent = data['component'][0]['globalList'] for area in continent: ws_foreign = wb.create_sheet(area['area'] + '疫情数据') ws_foreign.append(['国家', '现有确诊', '累计确诊', '累计治愈', '累计死亡', '累计确诊增量']) for country in area['subList']: ws_foreign.append([country['country'], country['curConfirm'], country['confirmed'], country['crued'], country['died'], country['confirmedRelative']]) # 在“全球各国疫情数据”和“亚洲疫情数据”两张表中写入中国疫情数据 ws1, ws2 = wb['全球各国疫情数据'], wb['亚洲疫情数据'] original_data = data['component'][0]['summaryDataIn'] add_china_data = ['中国', original_data['curConfirm'], original_data['confirmed'], original_data['cured'], original_data['died'], original_data['confirmedRelative']] ws1.append(add_china_data) ws2.append(add_china_data) \"\"\" 2、全球疫情数据更新时间:foreignLastUpdatedTime \"\"\" time_foreign = data['component'][0]['foreignLastUpdatedTime'] ws_time = wb.create_sheet('全球疫情数据更新时间') ws_time.column_dimensions['A'].width = 22 # 调整列宽 ws_time.append(['全球疫情数据更新时间']) ws_time.append([time_foreign]) wb.save('COVID-19-Global.xlsx') print('全球疫情数据已保存至 COVID-19-Global.xlsx!') 【5x04】中国每日数据 china_daily_data()1234567891011121314151617181920212223242526272829303132333435363738394041424344def china_daily_data(data): \"\"\" i_dict = data['component'][0]['trend'] i_dict['updateDate']:日期 i_dict['list'][0]:确诊 i_dict['list'][1]:疑似 i_dict['list'][2]:治愈 i_dict['list'][3]:死亡 i_dict['list'][4]:新增确诊 i_dict['list'][5]:新增疑似 i_dict['list'][6]:新增治愈 i_dict['list'][7]:新增死亡 i_dict['list'][8]:累计境外输入 i_dict['list'][9]:新增境外输入 \"\"\" ccd_dict = data['component'][0]['trend'] update_date = ccd_dict['updateDate'] # 日期 china_confirmed = ccd_dict['list'][0]['data'] # 每日累计确诊数据 china_crued = ccd_dict['list'][2]['data'] # 每日累计治愈数据 china_died = ccd_dict['list'][3]['data'] # 每日累计死亡数据 wb = openpyxl.load_workbook('COVID-19-China.xlsx') # 写入每日累计确诊数据 ws_china_confirmed = wb.create_sheet('中国每日累计确诊数据') ws_china_confirmed.append(['日期', '数据']) for data in zip(update_date, china_confirmed): ws_china_confirmed.append(data) # 写入每日累计治愈数据 ws_china_crued = wb.create_sheet('中国每日累计治愈数据') ws_china_crued.append(['日期', '数据']) for data in zip(update_date, china_crued): ws_china_crued.append(data) # 写入每日累计死亡数据 ws_china_died = wb.create_sheet('中国每日累计死亡数据') ws_china_died.append(['日期', '数据']) for data in zip(update_date, china_died): ws_china_died.append(data) wb.save('COVID-19-China.xlsx') print('中国每日累计确诊/治愈/死亡数据已保存至 COVID-19-China.xlsx!') 【5x05】境外每日数据 foreign_daily_data()123456789101112131415161718192021222324252627282930313233343536373839def foreign_daily_data(data): \"\"\" te_dict = data['component'][0]['allForeignTrend'] te_dict['updateDate']:日期 te_dict['list'][0]:累计确诊 te_dict['list'][1]:治愈 te_dict['list'][2]:死亡 te_dict['list'][3]:现有确诊 te_dict['list'][4]:新增确诊 \"\"\" te_dict = data['component'][0]['allForeignTrend'] update_date = te_dict['updateDate'] # 日期 foreign_confirmed = te_dict['list'][0]['data'] # 每日累计确诊数据 foreign_crued = te_dict['list'][1]['data'] # 每日累计治愈数据 foreign_died = te_dict['list'][2]['data'] # 每日累计死亡数据 wb = openpyxl.load_workbook('COVID-19-Global.xlsx') # 写入每日累计确诊数据 ws_foreign_confirmed = wb.create_sheet('境外每日累计确诊数据') ws_foreign_confirmed.append(['日期', '数据']) for data in zip(update_date, foreign_confirmed): ws_foreign_confirmed.append(data) # 写入累计治愈数据 ws_foreign_crued = wb.create_sheet('境外每日累计治愈数据') ws_foreign_crued.append(['日期', '数据']) for data in zip(update_date, foreign_crued): ws_foreign_crued.append(data) # 写入累计死亡数据 ws_foreign_died = wb.create_sheet('境外每日累计死亡数据') ws_foreign_died.append(['日期', '数据']) for data in zip(update_date, foreign_died): ws_foreign_died.append(data) wb.save('COVID-19-Global.xlsx') print('境外每日累计确诊/治愈/死亡数据已保存至 COVID-19-Global.xlsx!') 【6x00】词云图绘制模块 data_wordcloud【6x01】中国累计确诊词云图 foreign_daily_data()1234567891011121314def china_wordcloud(): wb = openpyxl.load_workbook('COVID-19-China.xlsx') # 获取已有的xlsx文件 ws_china = wb['中国省份疫情数据'] # 获取中国省份疫情数据表 ws_china.delete_rows(1) # 删除第一行 china_dict = {} # 将省份及其累计确诊按照键值对形式储存在字典中 for data in ws_china.values: china_dict[data[0]] = int(data[2]) word_cloud = wordcloud.WordCloud(font_path='C:/Windows/Fonts/simsun.ttc', background_color='#CDC9C9', min_font_size=15, width=900, height=500) word_cloud.generate_from_frequencies(china_dict) word_cloud.to_file('WordCloud-China.png') print('中国省份疫情词云图绘制完毕!') 【6x02】全球累计确诊词云图 foreign_daily_data()12345678910111213def global_wordcloud(): wb = openpyxl.load_workbook('COVID-19-Global.xlsx') ws_global = wb['全球各国疫情数据'] ws_global.delete_rows(1) global_dict = {} for data in ws_global.values: global_dict[data[0]] = int(data[2]) word_cloud = wordcloud.WordCloud(font_path='C:/Windows/Fonts/simsun.ttc', background_color='#CDC9C9', width=900, height=500) word_cloud.generate_from_frequencies(global_dict) word_cloud.to_file('WordCloud-Global.png') print('全球各国疫情词云图绘制完毕!') 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/107140534未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【7x00】地图绘制模块 data_map【7x01】中国累计确诊地图 china_total_map()123456789101112131415161718192021222324252627282930313233def china_total_map(): wb = openpyxl.load_workbook('COVID-19-China.xlsx') # 获取已有的xlsx文件 ws_time = wb['中国疫情数据更新时间'] # 获取文件中中国疫情数据更新时间表 ws_data = wb['中国省份疫情数据'] # 获取文件中中国省份疫情数据表 ws_data.delete_rows(1) # 删除第一行 province = [] # 省份 curconfirm = [] # 累计确诊 for data in ws_data.values: province.append(data[0]) curconfirm.append(data[2]) time_china = ws_time['A2'].value # 更新时间 # 设置分级颜色 pieces = [ {'max': 0, 'min': 0, 'label': '0', 'color': '#FFFFFF'}, {'max': 9, 'min': 1, 'label': '1-9', 'color': '#FFE5DB'}, {'max': 99, 'min': 10, 'label': '10-99', 'color': '#FF9985'}, {'max': 999, 'min': 100, 'label': '100-999', 'color': '#F57567'}, {'max': 9999, 'min': 1000, 'label': '1000-9999', 'color': '#E64546'}, {'max': 99999, 'min': 10000, 'label': '≧10000', 'color': '#B80909'} ] # 绘制地图 ct_map = ( Map() .add(series_name='累计确诊人数', data_pair=[list(z) for z in zip(province, curconfirm)], maptype=\"china\") .set_global_opts( title_opts=opts.TitleOpts(title=\"中国疫情数据(累计确诊)\", subtitle='数据更新至:' + time_china + '\\n\\n来源:百度疫情实时大数据报告'), visualmap_opts=opts.VisualMapOpts(max_=300, is_piecewise=True, pieces=pieces) ) ) return ct_map 【7x02】全球累计确诊地图 global_total_map()123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253def global_total_map(): wb = openpyxl.load_workbook('COVID-19-Global.xlsx') ws_time = wb['全球疫情数据更新时间'] ws_data = wb['全球各国疫情数据'] ws_data.delete_rows(1) country = [] # 国家 curconfirm = [] # 累计确诊 for data in ws_data.values: country.append(data[0]) curconfirm.append(data[2]) time_global = ws_time['A2'].value # 更新时间 # 国家名称中英文映射表 name_map = { \"Somalia\": \"索马里\", \"Liechtenstein\": \"列支敦士登\", \"Morocco\": \"摩洛哥\", \"W. Sahara\": \"西撒哈拉\", \"Serbia\": \"塞尔维亚\", \"Afghanistan\": \"阿富汗\", \"Angola\": \"安哥拉\", \"Albania\": \"阿尔巴尼亚\", \"Andorra\": \"安道尔共和国\", \"United Arab Emirates\": \"阿拉伯联合酋长国\", \"Argentina\": \"阿根廷\", \"Armenia\": \"亚美尼亚\", \"Australia\": \"澳大利亚\", \"Austria\": \"奥地利\", \"Azerbaijan\": \"阿塞拜疆\", \"Burundi\": \"布隆迪\", \"Belgium\": \"比利时\", \"Benin\": \"贝宁\", \"Burkina Faso\": \"布基纳法索\", \"Bangladesh\": \"孟加拉国\", \"Bulgaria\": \"保加利亚\", \"Bahrain\": \"巴林\", \"Bahamas\": \"巴哈马\", \"Bosnia and Herz.\": \"波斯尼亚和黑塞哥维那\", \"Belarus\": \"白俄罗斯\", \"Belize\": \"伯利兹\", \"Bermuda\": \"百慕大\", \"Bolivia\": \"玻利维亚\", \"Brazil\": \"巴西\", \"Barbados\": \"巴巴多斯\", \"Brunei\": \"文莱\", \"Bhutan\": \"不丹\", \"Botswana\": \"博茨瓦纳\", \"Central African Rep.\": \"中非共和国\", \"Canada\": \"加拿大\", \"Switzerland\": \"瑞士\", \"Chile\": \"智利\", \"China\": \"中国\", \"Côte d'Ivoire\": \"科特迪瓦\", \"Cameroon\": \"喀麦隆\", \"Dem. Rep. Congo\": \"刚果(布)\", \"Congo\": \"刚果(金)\", \"Colombia\": \"哥伦比亚\", \"Cape Verde\": \"佛得角\", \"Costa Rica\": \"哥斯达黎加\", \"Cuba\": \"古巴\", \"N. Cyprus\": \"北塞浦路斯\", \"Cyprus\": \"塞浦路斯\", \"Czech Rep.\": \"捷克\", \"Germany\": \"德国\", \"Djibouti\": \"吉布提\", \"Denmark\": \"丹麦\", \"Dominican Rep.\": \"多米尼加\", \"Algeria\": \"阿尔及利亚\", \"Ecuador\": \"厄瓜多尔\", \"Egypt\": \"埃及\", \"Eritrea\": \"厄立特里亚\", \"Spain\": \"西班牙\", \"Estonia\": \"爱沙尼亚\", \"Ethiopia\": \"埃塞俄比亚\", \"Finland\": \"芬兰\", \"Fiji\": \"斐济\", \"France\": \"法国\", \"Gabon\": \"加蓬\", \"United Kingdom\": \"英国\", \"Georgia\": \"格鲁吉亚\", \"Ghana\": \"加纳\", \"Guinea\": \"几内亚\", \"Gambia\": \"冈比亚\", \"Guinea-Bissau\": \"几内亚比绍\", \"Eq. Guinea\": \"赤道几内亚\", \"Greece\": \"希腊\", \"Grenada\": \"格林纳达\", \"Greenland\": \"格陵兰岛\", \"Guatemala\": \"危地马拉\", \"Guam\": \"关岛\", \"Guyana\": \"圭亚那合作共和国\", \"Honduras\": \"洪都拉斯\", \"Croatia\": \"克罗地亚\", \"Haiti\": \"海地\", \"Hungary\": \"匈牙利\", \"Indonesia\": \"印度尼西亚\", \"India\": \"印度\", \"Br. Indian Ocean Ter.\": \"英属印度洋领土\", \"Ireland\": \"爱尔兰\", \"Iran\": \"伊朗\", \"Iraq\": \"伊拉克\", \"Iceland\": \"冰岛\", \"Israel\": \"以色列\", \"Italy\": \"意大利\", \"Jamaica\": \"牙买加\", \"Jordan\": \"约旦\", \"Japan\": \"日本\", \"Siachen Glacier\": \"锡亚琴冰川\", \"Kazakhstan\": \"哈萨克斯坦\", \"Kenya\": \"肯尼亚\", \"Kyrgyzstan\": \"吉尔吉斯斯坦\", \"Cambodia\": \"柬埔寨\", \"Korea\": \"韩国\", \"Kuwait\": \"科威特\", \"Lao PDR\": \"老挝\", \"Lebanon\": \"黎巴嫩\", \"Liberia\": \"利比里亚\", \"Libya\": \"利比亚\", \"Sri Lanka\": \"斯里兰卡\", \"Lesotho\": \"莱索托\", \"Lithuania\": \"立陶宛\", \"Luxembourg\": \"卢森堡\", \"Latvia\": \"拉脱维亚\", \"Moldova\": \"摩尔多瓦\", \"Madagascar\": \"马达加斯加\", \"Mexico\": \"墨西哥\", \"Macedonia\": \"马其顿\", \"Mali\": \"马里\", \"Malta\": \"马耳他\", \"Myanmar\": \"缅甸\", \"Montenegro\": \"黑山\", \"Mongolia\": \"蒙古国\", \"Mozambique\": \"莫桑比克\", \"Mauritania\": \"毛里塔尼亚\", \"Mauritius\": \"毛里求斯\", \"Malawi\": \"马拉维\", \"Malaysia\": \"马来西亚\", \"Namibia\": \"纳米比亚\", \"New Caledonia\": \"新喀里多尼亚\", \"Niger\": \"尼日尔\", \"Nigeria\": \"尼日利亚\", \"Nicaragua\": \"尼加拉瓜\", \"Netherlands\": \"荷兰\", \"Norway\": \"挪威\", \"Nepal\": \"尼泊尔\", \"New Zealand\": \"新西兰\", \"Oman\": \"阿曼\", \"Pakistan\": \"巴基斯坦\", \"Panama\": \"巴拿马\", \"Peru\": \"秘鲁\", \"Philippines\": \"菲律宾\", \"Papua New Guinea\": \"巴布亚新几内亚\", \"Poland\": \"波兰\", \"Puerto Rico\": \"波多黎各\", \"Dem. Rep. Korea\": \"朝鲜\", \"Portugal\": \"葡萄牙\", \"Paraguay\": \"巴拉圭\", \"Palestine\": \"巴勒斯坦\", \"Qatar\": \"卡塔尔\", \"Romania\": \"罗马尼亚\", \"Russia\": \"俄罗斯\", \"Rwanda\": \"卢旺达\", \"Saudi Arabia\": \"沙特阿拉伯\", \"Sudan\": \"苏丹\", \"S. Sudan\": \"南苏丹\", \"Senegal\": \"塞内加尔\", \"Singapore\": \"新加坡\", \"Solomon Is.\": \"所罗门群岛\", \"Sierra Leone\": \"塞拉利昂\", \"El Salvador\": \"萨尔瓦多\", \"Suriname\": \"苏里南\", \"Slovakia\": \"斯洛伐克\", \"Slovenia\": \"斯洛文尼亚\", \"Sweden\": \"瑞典\", \"Swaziland\": \"斯威士兰\", \"Seychelles\": \"塞舌尔\", \"Syria\": \"叙利亚\", \"Chad\": \"乍得\", \"Togo\": \"多哥\", \"Thailand\": \"泰国\", \"Tajikistan\": \"塔吉克斯坦\", \"Turkmenistan\": \"土库曼斯坦\", \"Timor-Leste\": \"东帝汶\", \"Tonga\": \"汤加\", \"Trinidad and Tobago\": \"特立尼达和多巴哥\", \"Tunisia\": \"突尼斯\", \"Turkey\": \"土耳其\", \"Tanzania\": \"坦桑尼亚\", \"Uganda\": \"乌干达\", \"Ukraine\": \"乌克兰\", \"Uruguay\": \"乌拉圭\", \"United States\": \"美国\", \"Uzbekistan\": \"乌兹别克斯坦\", \"Venezuela\": \"委内瑞拉\", \"Vietnam\": \"越南\", \"Vanuatu\": \"瓦努阿图\", \"Yemen\": \"也门\", \"South Africa\": \"南非\", \"Zambia\": \"赞比亚\", \"Zimbabwe\": \"津巴布韦\", \"Aland\": \"奥兰群岛\", \"American Samoa\": \"美属萨摩亚\", \"Fr. S. Antarctic Lands\": \"南极洲\", \"Antigua and Barb.\": \"安提瓜和巴布达\", \"Comoros\": \"科摩罗\", \"Curaçao\": \"库拉索岛\", \"Cayman Is.\": \"开曼群岛\", \"Dominica\": \"多米尼加\", \"Falkland Is.\": \"福克兰群岛马尔维纳斯\", \"Faeroe Is.\": \"法罗群岛\", \"Micronesia\": \"密克罗尼西亚\", \"Heard I. and McDonald Is.\": \"赫德岛和麦克唐纳群岛\", \"Isle of Man\": \"曼岛\", \"Jersey\": \"泽西岛\", \"Kiribati\": \"基里巴斯\", \"Saint Lucia\": \"圣卢西亚\", \"N. Mariana Is.\": \"北马里亚纳群岛\", \"Montserrat\": \"蒙特塞拉特\", \"Niue\": \"纽埃\", \"Palau\": \"帕劳\", \"Fr. Polynesia\": \"法属波利尼西亚\", \"S. Geo. and S. Sandw. Is.\": \"南乔治亚岛和南桑威奇群岛\", \"Saint Helena\": \"圣赫勒拿\", \"St. Pierre and Miquelon\": \"圣皮埃尔和密克隆群岛\", \"São Tomé and Principe\": \"圣多美和普林西比\", \"Turks and Caicos Is.\": \"特克斯和凯科斯群岛\", \"St. Vin. and Gren.\": \"圣文森特和格林纳丁斯\", \"U.S. Virgin Is.\": \"美属维尔京群岛\", \"Samoa\": \"萨摩亚\" } pieces = [ {'max': 0, 'min': 0, 'label': '0', 'color': '#FFFFFF'}, {'max': 49, 'min': 1, 'label': '1-49', 'color': '#FFE5DB'}, {'max': 99, 'min': 50, 'label': '50-99', 'color': '#FFC4B3'}, {'max': 999, 'min': 100, 'label': '100-999', 'color': '#FF9985'}, {'max': 9999, 'min': 1000, 'label': '1000-9999', 'color': '#F57567'}, {'max': 99999, 'min': 10000, 'label': '10000-99999', 'color': '#E64546'}, {'max': 999999, 'min': 100000, 'label': '100000-999999', 'color': '#B80909'}, {'max': 9999999, 'min': 1000000, 'label': '≧1000000', 'color': '#8A0808'} ] gt_map = ( Map() .add(series_name='累计确诊人数', data_pair=[list(z) for z in zip(country, curconfirm)], maptype=\"world\", name_map=name_map, is_map_symbol_show=False) .set_series_opts(label_opts=opts.LabelOpts(is_show=False)) .set_global_opts( title_opts=opts.TitleOpts(title=\"全球疫情数据(累计确诊)\", subtitle='数据更新至:' + time_global + '\\n\\n来源:百度疫情实时大数据报告'), visualmap_opts=opts.VisualMapOpts(max_=300, is_piecewise=True, pieces=pieces), ) ) return gt_map 【7x03】中国每日数据折线图 china_daily_map()123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354def china_daily_map(): wb = openpyxl.load_workbook('COVID-19-China.xlsx') ws_china_confirmed = wb['中国每日累计确诊数据'] ws_china_crued = wb['中国每日累计治愈数据'] ws_china_died = wb['中国每日累计死亡数据'] ws_china_confirmed.delete_rows(1) ws_china_crued.delete_rows(1) ws_china_died.delete_rows(1) x_date = [] # 日期 y_china_confirmed = [] # 每日累计确诊 y_china_crued = [] # 每日累计治愈 y_china_died = [] # 每日累计死亡 for china_confirmed in ws_china_confirmed.values: y_china_confirmed.append(china_confirmed[1]) for china_crued in ws_china_crued.values: x_date.append(china_crued[0]) y_china_crued.append(china_crued[1]) for china_died in ws_china_died.values: y_china_died.append(china_died[1]) fi_map = ( Line(init_opts=opts.InitOpts(height='420px')) .add_xaxis(xaxis_data=x_date) .add_yaxis( series_name=\"中国累计确诊数据\", y_axis=y_china_confirmed, label_opts=opts.LabelOpts(is_show=False), ) .add_yaxis( series_name=\"中国累计治愈趋势\", y_axis=y_china_crued, label_opts=opts.LabelOpts(is_show=False), ) .add_yaxis( series_name=\"中国累计死亡趋势\", y_axis=y_china_died, label_opts=opts.LabelOpts(is_show=False), ) .set_global_opts( title_opts=opts.TitleOpts(title=\"中国每日累计确诊/治愈/死亡趋势\"), legend_opts=opts.LegendOpts(pos_bottom=\"bottom\", orient='horizontal'), tooltip_opts=opts.TooltipOpts(trigger=\"axis\"), yaxis_opts=opts.AxisOpts( type_=\"value\", axistick_opts=opts.AxisTickOpts(is_show=True), splitline_opts=opts.SplitLineOpts(is_show=True), ), xaxis_opts=opts.AxisOpts(type_=\"category\", boundary_gap=False), ) ) return fi_map 【7x04】境外每日数据折线图 foreign_daily_map()123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354def foreign_daily_map(): wb = openpyxl.load_workbook('COVID-19-Global.xlsx') ws_foreign_confirmed = wb['境外每日累计确诊数据'] ws_foreign_crued = wb['境外每日累计治愈数据'] ws_foreign_died = wb['境外每日累计死亡数据'] ws_foreign_confirmed.delete_rows(1) ws_foreign_crued.delete_rows(1) ws_foreign_died.delete_rows(1) x_date = [] # 日期 y_foreign_confirmed = [] # 累计确诊 y_foreign_crued = [] # 累计治愈 y_foreign_died = [] # 累计死亡 for foreign_confirmed in ws_foreign_confirmed.values: y_foreign_confirmed.append(foreign_confirmed[1]) for foreign_crued in ws_foreign_crued.values: x_date.append(foreign_crued[0]) y_foreign_crued.append(foreign_crued[1]) for foreign_died in ws_foreign_died.values: y_foreign_died.append(foreign_died[1]) fte_map = ( Line(init_opts=opts.InitOpts(height='420px')) .add_xaxis(xaxis_data=x_date) .add_yaxis( series_name=\"境外累计确诊趋势\", y_axis=y_foreign_confirmed, label_opts=opts.LabelOpts(is_show=False), ) .add_yaxis( series_name=\"境外累计治愈趋势\", y_axis=y_foreign_crued, label_opts=opts.LabelOpts(is_show=False), ) .add_yaxis( series_name=\"境外累计死亡趋势\", y_axis=y_foreign_died, label_opts=opts.LabelOpts(is_show=False), ) .set_global_opts( title_opts=opts.TitleOpts(title=\"境外每日累计确诊/治愈/死亡趋势\"), legend_opts=opts.LegendOpts(pos_bottom=\"bottom\", orient='horizontal'), tooltip_opts=opts.TooltipOpts(trigger=\"axis\"), yaxis_opts=opts.AxisOpts( type_=\"value\", axistick_opts=opts.AxisTickOpts(is_show=True), splitline_opts=opts.SplitLineOpts(is_show=True), ), xaxis_opts=opts.AxisOpts(type_=\"category\", boundary_gap=False), ) ) return fte_map 【8x00】结果截图【8x01】数据储存 Excel 【8x02】词云图 【8x03】地图 + 折线图 【9x00】完整代码预览地址:http://cov.itrhx.com/ 完整代码地址(点亮 star 有 buff 加成):https://github.com/TRHX/Python3-Spider-Practice/tree/master/COVID-19 其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice 爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/107140534未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫实战","slug":"Python3-学习笔记/爬虫实战","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫实战/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"肺炎疫情","slug":"肺炎疫情","permalink":"https://www.itrhx.com/tags/肺炎疫情/"}]},{"title":"Python 数据分析三剑客之 Pandas(十):数据读写","slug":"A88-Pandas-10","date":"2020-06-26T15:13:58.975Z","updated":"2020-08-06T03:33:37.232Z","comments":true,"path":"2020/06/26/A88-Pandas-10/","link":"","permalink":"https://www.itrhx.com/2020/06/26/A88-Pandas-10/","excerpt":"","text":"Pandas 系列文章: Python 数据分析三剑客之 Pandas(一):认识 Pandas 及其 Series、DataFrame 对象 Python 数据分析三剑客之 Pandas(二):Index 索引对象以及各种索引操作 Python 数据分析三剑客之 Pandas(三):算术运算与缺失值的处理 Python 数据分析三剑客之 Pandas(四):函数应用、映射、排序和层级索引 Python 数据分析三剑客之 Pandas(五):统计计算与统计描述 Python 数据分析三剑客之 Pandas(六):GroupBy 数据分裂、应用与合并 Python 数据分析三剑客之 Pandas(七):合并数据集 Python 数据分析三剑客之 Pandas(八):数据重塑、重复数据处理与数据替换 Python 数据分析三剑客之 Pandas(九):时间序列 Python 数据分析三剑客之 Pandas(十):数据读写 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106963135未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【01x00】读取数据Pandas 提供了一些用于将表格型数据读取为 DataFrame 对象的函数。常见方法如下: Pandas 官方对 IO 工具的介绍:https://pandas.pydata.org/pandas-docs/stable/user_guide/io.html 函数 描述 read_csv 从文件、URL、文件型对象中加载带分隔符的数据。默认分隔符为逗号 read_table 从文件、URL、文件型对象中加载带分隔符的数据。默认分隔符为制表符('\\t') read_fwf 读取定宽列格式数据(没有分隔符) read_clipboard 读取剪贴板中的数据,可以看做 read_table 的剪贴板版本。在将网页转换为表格时很有用 read_excel 从 Excel XLS 或 XLSX file 读取表格数据 read_hdf 读取 pandas写的 HDF5 文件 read_html 读取 HTML 文档中的所有表格 read_json 读取 JSON( JavaScript Object Notation)字符串中的数据 read_msgpack 读取二进制格式编码的 pandas 数据(Pandas v1.0.0 中已删除对 msgpack 的支持,建议使用 pyarrow) read_pickle 读取 Python pickle 格式中存储的任意对象 read_sas 读取存储于 SAS 系统自定义存储格式的 SAS 数据集 read_sql (使用 SQLAlchemy)读取 SQL 查询结果为 pandas 的 DataFrame read_stata 读取 Stata 文件格式的数据集 read_feather 读取 Feather 二进制格式文件 以下以 read_csv 和 read_table 为例,它们的参数多达 50 多个,具体可参见官方文档: read_csv:https://pandas.pydata.org/docs/reference/api/pandas.read_csv.html read_table:https://pandas.pydata.org/docs/reference/api/pandas.read_table.html 常用参数: 参数 描述 path 表示文件系统位置、URL、文件型对象的字符串 sep / delimiter 用于对行中各字段进行拆分的字符序列或正则表达式 header 用作列名的行号,默认为 0(第一行),如果没有 header 行就应该设置为 None index_col 用作行索引的列编号或列名。可以是单个名称、数字或由多个名称、数字组成的列表(层次化索引) names 用于结果的列名列表,结合 header=None skiprows 需要忽略的行数(从文件开始处算起),或需要跳过的行号列表(从0开始) na_values 指定一组值,将该组值设置为 NaN(缺失值) comment 用于将注释信息从行尾拆分出去的字符(一个或多个) parse_dates 尝试将数据解析为日期,默认为 False。如果为 True,则尝试解析所有列。此外,还可以指定需要解析的一组列号或列名。如果列表的元素为列表或元组,就会将多个列组合到一起再进行日期解析工作(例如,日期、时间分别位于两个列中) keep_date_col 如果连接多列解析日期,则保持参与连接的列。默认为 False converters 由列号 / 列名跟函数之间的映射关系组成的字典。例如,{'foo': f} 会对 foo 列的所有值应用函数 f dayfirst 当解析有歧义的日期时,将其看做国际格式(例如,7/6/2012 —> June 7,2012),默认为 Fase date_parser 用于解析日期的函数 nrows 需要读取的行数(从文件开始处算起) iterator 返回一个 TextParser 以便逐块读取文件 chunksize 文件块的大小(用于迭代) skip_footer 需要忽略的行数(从文件末尾处算起) verbose 打印各种解析器输出信息,比如“非数值列中缺失值的数量”等 encoding 用于 unicode 的文本编码格式。例如,“utf-8” 表示用 UTF-8 编码的文本 squeeze 如果数据经解析后仅含一列,则返回 Series thousands 千分位分隔符,如 , 或 . 【01x01】简单示例首先创建一个 test1.csv 文件: 使用 read_csv 方法将其读出为一个 DataFrame 对象: 12345678910>>> import pandas as pd>>> obj = pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test1.csv')>>> obj a b c d message0 1 2 3 4 hello1 5 6 7 8 world2 9 10 11 12 python>>> >>> type(obj)<class 'pandas.core.frame.DataFrame'> 前面的 csv 文件是以逗号分隔的,可以使用 read_table 方法并指定分隔符来读取: 1234567>>> import pandas as pd>>> obj = pd.read_table(r'C:\\Users\\TanRe\\Desktop\\test1.csv', sep=',')>>> obj a b c d message0 1 2 3 4 hello1 5 6 7 8 world2 9 10 11 12 python 【01x02】header / names 定制列标签以上示例中第一行为列标签,如果没有单独定义列标签,使用 read_csv 方法也会默认将第一行当作列标签: 123456>>> import pandas as pd>>> obj = pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test2.csv')>>> obj 1 2 3 4 hello0 5 6 7 8 world1 9 10 11 12 python 避免以上情况,可以设置 header=None,Pandas 会为其自动分配列标签: 123456>>> import pandas as pd>>> pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test2.csv', header=None) 0 1 2 3 40 1 2 3 4 hello1 5 6 7 8 world2 9 10 11 12 python 也可以使用 names 参数自定义列标签,传递的是一个列表: 123456>>> import pandas as pd>>> pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test2.csv', names=['a', 'b', 'c', 'd', 'message']) a b c d message0 1 2 3 4 hello1 5 6 7 8 world2 9 10 11 12 python 【01x03】index_col 指定列为行索引index_col 参数可以指定某一列作为 DataFrame 的行索引,传递的参数是列名称,在以下示例中,会将列名为 message 的列作为 DataFrame 的行索引: 12345678>>> pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test2.csv', names=['a', 'b', 'c', 'd', 'message'], index_col='message') a b c dmessage hello 1 2 3 4world 5 6 7 8python 9 10 11 12 如果需要构造多层索引的 DataFrame 对象,则只需传入由列编号或列名组成的列表即可: 123456789101112>>> import pandas as pd>>> pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test3.csv', index_col=['key1', 'key2']) value1 value2key1 key2 one a 1 2 b 3 4 c 5 6 d 7 8two a 9 10 b 11 12 c 13 14 d 15 16 【01x04】sep 指定分隔符在 read_table 中,sep 参数用于接收分隔符,如果遇到不是用固定的分隔符去分隔字段的,也可以传递一个正则表达式作为 read_table 的分隔符,如下面的 txt 文件数据之间是由不同的空白字符间隔开的: 1234567>>> import pandas as pd>>> pd.read_table(r'C:\\Users\\TanRe\\Desktop\\test1.txt', sep='\\s+') A B Caaa -0.264438 -1.026059 -0.619500bbb 0.927272 0.302904 -0.032399ccc -0.264273 -0.386314 -0.217601ddd -0.871858 -0.348382 1.100491 【01x05】skiprows 忽略行skiprows 参数可用于设置需要忽略的行数,或需要跳过的行号列表,在下面的示例中,读取文件时选择跳过第1、3、4行(索引值分别为0、2、3): 123456>>> import pandas as pd>>> pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test4.csv', skiprows=[0, 2, 3]) a b c d message0 1 2 3 4 hello1 5 6 7 8 world2 9 10 11 12 python 【01x06】na_values 设置缺失值当文件中出现了空字符串或者 NA 值,Pandas 会将其标记成 NaN(缺失值),同样也可以使用 isnull 方法来判断结果值是否为缺失值: 12345678910111213>>> import pandas as pd>>> obj = pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test5.csv')>>> obj something a b c d message0 one 1 2 3.0 4 NaN1 two 5 6 NaN 8 world2 three 9 10 11.0 12 python>>> >>> pd.isnull(obj) something a b c d message0 False False False False False True1 False False False True False False2 False False False False False False na_values 方法可以传递一组值,将这组值设置为缺失值,如果传递的为字典对象,则字典的各值将被设置为 NaN: 12345678910111213141516171819202122>>> import pandas as pd>>> obj1 = pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test5.csv')>>> obj1 something a b c d message0 one 1 2 3.0 4 NaN1 two 5 6 NaN 8 world2 three 9 10 11.0 12 python>>> >>> obj2 = pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test5.csv', na_values=['1', '12'])>>> obj2 something a b c d message0 one NaN 2 3.0 4.0 NaN1 two 5.0 6 NaN 8.0 world2 three 9.0 10 11.0 NaN python>>> >>> sentinels = {'message': ['python', 'world'], 'something': ['two']}>>> obj3 = pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test5.csv', na_values=sentinels)>>> obj3 something a b c d message0 one 1 2 3.0 4 NaN1 NaN 5 6 NaN 8 NaN2 three 9 10 11.0 12 NaN 【01x07】nrows / chunksize 行与块以下 test6.csv 文件中包含 50 行数据: 可以设置 pd.options.display.max_rows 来紧凑地显示指定行数的数据: 1234567891011121314151617>>> import pandas as pd>>> pd.options.display.max_rows = 10>>> pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test6.csv') one two three four key0 0.467976 -0.038649 -0.295344 -1.824726 L1 -0.358893 1.404453 0.704965 -0.200638 B2 -0.501840 0.659254 -0.421691 -0.057688 G3 0.204886 1.074134 1.388361 -0.982404 R4 0.354628 -0.133116 0.283763 -0.837063 Q.. ... ... ... ... ..45 2.311896 -0.417070 -1.409599 -0.515821 L46 -0.479893 -0.633419 0.745152 -0.646038 E47 0.523331 0.787112 0.486066 1.093156 K48 -0.362559 0.598894 -1.843201 0.887292 G49 -0.096376 -1.012999 -0.657431 -0.573315 0[50 rows x 5 columns] 通过 nrows 参数可以读取指定行数: 12345678>>> import pandas as pd>>> pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test6.csv', nrows=5) one two three four key0 0.467976 -0.038649 -0.295344 -1.824726 L1 -0.358893 1.404453 0.704965 -0.200638 B2 -0.501840 0.659254 -0.421691 -0.057688 G3 0.204886 1.074134 1.388361 -0.982404 R4 0.354628 -0.133116 0.283763 -0.837063 Q 要逐块读取文件,可以指定 chunksize(行数): 1234>>> import pandas as pd>>> chunker = pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test6.csv', chunksize=50)>>> chunker<pandas.io.parsers.TextFileReader object at 0x07A20D60> 返回的 TextParser 对象,可以根据 chunksize 对文件进行逐块迭代。以下示例中,对 test6.csv 文件数据进行迭代处理,将值计数聚合到 “key” 列中: 1234567891011121314151617181920>>> import pandas as pd>>> chunker = pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test6.csv', chunksize=50)>>> tot = pd.Series([], dtype='float64')>>> for piece in chunker: tot = tot.add(piece['key'].value_counts(), fill_value=0)>>> tot = tot.sort_values(ascending=False)>>> tot[:10]G 6.0E 5.0B 5.0L 5.00 5.0K 4.0A 4.0R 4.0C 2.0Q 2.0dtype: float64 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106963135未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【02x00】写入数据Pandas 提供了一些用于将表格型数据读取为 DataFrame 对象的函数。常见方法如下: 函数 描述 to_csv 将对象写入逗号分隔值(csv)文件 to_clipboard 将对象复制到系统剪贴板 to_excel 将对象写入 Excel 工作表 to_hdf 使用 HDFStore 将包含的数据写入 HDF5 文件 to_html 将 DataFrame 呈现为 HTML 表格 to_json 将对象转换为 JSON( JavaScript Object Notation)字符串 to_msgpack 将对象写入二进制格式编码的文件(Pandas v1.0.0 中已删除对 msgpack 的支持,建议使用 pyarrow) to_pickle Pickle(序列化)对象到文件 to_sql 将存储在 DataFrame 中的数据写入 SQL 数据库 to_stata 将 DataFrame 对象导出为 Stata 格式 to_feather 将 DataFrames 写入 Feather 二进制格式文件 以下以 to_csv 为例,它的参数同样多达 50 多个,具体可参见官方文档: https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.to_csv.html https://pandas.pydata.org/docs/reference/api/pandas.Series.to_csv.html 【02x01】简单示例以之前的 test5.csv 文件为例,先读出数据,再将数据写入另外的文件: 123456789>>> import pandas as pd>>> data = pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test5.csv')>>> data something a b c d message0 one 1 2 3.0 4 NaN1 two 5 6 NaN 8 world2 three 9 10 11.0 12 python>>> >>> data.to_csv(r'C:\\Users\\TanRe\\Desktop\\out1.csv') 【02x02】sep 指定分隔符sep 参数可用于其他分隔符: 123456789>>> import pandas as pd>>> data = pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test5.csv')>>> data something a b c d message0 one 1 2 3.0 4 NaN1 two 5 6 NaN 8 world2 three 9 10 11.0 12 python>>>>>> data.to_csv(r'C:\\Users\\TanRe\\Desktop\\out2.csv', sep='|') 【02x03】na_rep 替换缺失值na_rep 参数可将缺失值(NaN)替换成其他字符串: 123456789>>> import pandas as pd>>> data = pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test5.csv')>>> data something a b c d message0 one 1 2 3.0 4 NaN1 two 5 6 NaN 8 world2 three 9 10 11.0 12 python>>> >>> data.to_csv(r'C:\\Users\\TanRe\\Desktop\\out3.csv', na_rep='X') 【02x04】index / header 行与列标签设置 index=False, header=False,可以禁用行标签与列标签: 123456789>>> import pandas as pd>>> data = pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test5.csv')>>> data something a b c d message0 one 1 2 3.0 4 NaN1 two 5 6 NaN 8 world2 three 9 10 11.0 12 python>>> >>> data.to_csv(r'C:\\Users\\TanRe\\Desktop\\out4.csv', index=False, header=False) 还可以传入列表来重新设置列标签: 123456789>>> import pandas as pd>>> data = pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test5.csv')>>> data something a b c d message0 one 1 2 3.0 4 NaN1 two 5 6 NaN 8 world2 three 9 10 11.0 12 python>>> >>> data.to_csv(r'C:\\Users\\TanRe\\Desktop\\out5.csv', header=['a', 'b', 'c', 'd', 'e', 'f']) 【02x05】columns 指定列可以通过设置 columns 参数,只写入部分列,并按照指定顺序排序: 123456789>>> import pandas as pd>>> data = pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test5.csv')>>> data something a b c d message0 one 1 2 3.0 4 NaN1 two 5 6 NaN 8 world2 three 9 10 11.0 12 python>>>>>> data.to_csv(r'C:\\Users\\TanRe\\Desktop\\out6.csv', columns=['c', 'b', 'a']) 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106963135未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Pandas","slug":"Python-数据分析/Pandas","permalink":"https://www.itrhx.com/categories/Python-数据分析/Pandas/"}],"tags":[{"name":"IO操作","slug":"IO操作","permalink":"https://www.itrhx.com/tags/IO操作/"},{"name":"Pandas","slug":"Pandas","permalink":"https://www.itrhx.com/tags/Pandas/"},{"name":"数据读写","slug":"数据读写","permalink":"https://www.itrhx.com/tags/数据读写/"}]},{"title":"Python 数据分析三剑客之 Pandas(九):时间序列","slug":"A87-Pandas-09","date":"2020-06-25T14:03:28.198Z","updated":"2020-07-06T13:45:31.263Z","comments":true,"path":"2020/06/25/A87-Pandas-09/","link":"","permalink":"https://www.itrhx.com/2020/06/25/A87-Pandas-09/","excerpt":"","text":"Pandas 系列文章: Python 数据分析三剑客之 Pandas(一):认识 Pandas 及其 Series、DataFrame 对象 Python 数据分析三剑客之 Pandas(二):Index 索引对象以及各种索引操作 Python 数据分析三剑客之 Pandas(三):算术运算与缺失值的处理 Python 数据分析三剑客之 Pandas(四):函数应用、映射、排序和层级索引 Python 数据分析三剑客之 Pandas(五):统计计算与统计描述 Python 数据分析三剑客之 Pandas(六):GroupBy 数据分裂、应用与合并 Python 数据分析三剑客之 Pandas(七):合并数据集 Python 数据分析三剑客之 Pandas(八):数据重塑、重复数据处理与数据替换 Python 数据分析三剑客之 Pandas(九):时间序列 Python 数据分析三剑客之 Pandas(十):数据读写 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106947061未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【01x00】时间序列官网对于时间序列的介绍:https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html 时间序列(time series)是一种重要的结构化数据形式,应用于多个领域,包括金融学、经济学、生态学、神经科学、物理学等。在多个时间点观察或测量到的任何事物都可以形成一段时间序列。很多时间序列是固定频率的,也就是说,数据点是根据某种规律定期出现的(比如每15秒、每5分钟、每月出现一次)。时间序列也可以是不定期的,没有固定的时间单位或单位之间的偏移量。时间序列数据的意义取决于具体的应用场景,主要有以下几种: 时间戳(timestamp),表示某个具体的时间点,例如 2020-6-24 15:30; 固定周期(period),表示某个时间周期,例如 2020-01; 时间间隔(timedelta),持续时间,即两个日期或时间之间的差异。 针对时间戳数据,Pandas 提供了 Timestamp 类型。它本质上是 Python 的原生 datetime 类型的替代品,但是在性能更好的 numpy.datetime64 类型的基础上创建。对应的索引数据结构是 DatetimeIndex。 针对时间周期数据,Pandas 提供了 Period 类型。这是利用 numpy.datetime64 类型将固定频率的时间间隔进行编码。对应的索引数据结构是 PeriodIndex。 针对时间增量或持续时间,Pandas 提供了 Timedelta 类型。Timedelta 是一种代替 Python 原生datetime.timedelta 类型的高性能数据结构,同样是基于 numpy.timedelta64 类型。对应的索引数据结构是 TimedeltaIndex。 【02x00】Timestamp 时间戳【02x01】pandas.Timestamp在 pandas 中,pandas.Timestamp 方法用来代替 Python 中的 datetime.datetime 方法。 Timestamp 与 Python 的 Datetime 等效,在大多数情况下都可以互换。 此类型用于组成 DatetimeIndex 以及 Pandas 中其他面向时间序列的数据结构。 官方文档:https://pandas.pydata.org/docs/reference/api/pandas.Timestamp.html 基本语法: 12345class pandas.Timestamp(ts_input=<object object>, freq=None, tz=None, unit=None, year=None, month=None, day=None, hour=None, minute=None, second=None, microsecond=None, nanosecond=None, tzinfo=None) 常用参数: 参数 描述 ts_input 要转换为时间戳的对象,可以是 datetime-like,str,int,float 类型 freq 时间戳将具有的偏移量,可以是 str,日期偏移量类型,取值参见【02x02】freq 频率部分取值 tz 时间戳将具有的时区 unit 如果 ts_input 是整数或浮点数,该参数用于设置其单位(D、s、ms、us、ns) 简单示例: 123>>> import pandas as pd>>> pd.Timestamp('2017-01-01T12')Timestamp('2017-01-01 12:00:00') 设置 unit='s',即待转换对象单位为秒: 123>>> import pandas as pd>>> pd.Timestamp(1513393355.5, unit='s')Timestamp('2017-12-16 03:02:35.500000') 使用 tz 参数设置时区: 123>>> import pandas as pd>>> pd.Timestamp(1513393355, unit='s', tz='US/Pacific')Timestamp('2017-12-15 19:02:35-0800', tz='US/Pacific') 单独设置年月日: 123>>> import pandas as pd>>> pd.Timestamp(year=2020, month=6, day=24, hour=12)Timestamp('2020-06-24 12:00:00') 【02x02】freq 频率部分取值完整取值参见官方文档:https://pandas.pydata.org/docs/user_guide/timeseries.html#timeseries-offset-aliases 参数 类型 描述 D Day 每日历日 B BusinessDay 每工作日 H Hour 每小时 T 或 min Minute 每分 S Second 每秒 L 或 ms Milli 每毫秒(即每千分之一秒) U Micro 每微秒(即每百万分之一秒) M MonthEnd 每月最后一个日历日 BM BusinessMonthEnd 每月最后一个工作日 MS MonthBegin 每月第一个日历日 BMS BusinessMonthBegin 每月第一个工作日 W-MON、W-TUE… Week 从指定的星期几(MON、TUE、 WED、THU、FR、SAT、SUN)开始算起,每周 WoM-1MON、WOM-2MON… WeekOfMonth 产生每月第一、第二、第三或第四周的星期几。例如,WoM-3FRI 表示每月第3个星期五 Q-JAN、Q-FEB… QuarterEnd 对于以指定月份(JAN、FEB、MAR、APR、MAY、JUN、JUL、AUG、SEP、OCT、NOV、DEC)结束的年度,每季度最后一月的最后个日历日 BQ-JAN、BQ-FEB… BusinessQuarterEnd 对于以指定月份结束的年度,每季度最后一月的最后一个工作日 QS-JAN、QS-FEB… QuarterBegin 对于以指定月份结束的年度,每季度最后一月的第一个日历日 BQS-JAN、 BQS-FEB… BusinessQuarterBegin 对于以指定月份结束的年度,每季度最后一月的第一个工作日 A-JAN、A-FEB… YearEnd 每年指定月份(JAN、FEB、MAR、APR、MAY、JUN、JUL、AUG、SEP、 OCT、NOV、DEC)的最后一个日历日 BA-JAN、BA-FEB… BusinessYearEnd 每年指定月份的最后一个工作日 AS-JAN、AS-FEB… YearBegin 每年指定月份的第一个历日日 BAS-JAN、BAS-FEB… BusinessYearBegin 每年指定月份的第一个工作日 【02x03】to_datetime在 Python 中,datetime 库提供了日期和时间处理方法,利用 str 或 strftime 方法可以将 datetime 对象转化成字符串,具体用法可参见【Python 标准库学习】日期和时间处理库 — datetime。 12345678910>>> from datetime import datetime>>> stamp = datetime(2020, 6, 24)>>> stampdatetime.datetime(2020, 6, 24, 0, 0)>>>>>> str(stamp)'2020-06-24 00:00:00'>>> >>> stamp.strftime('%Y-%m-%d')'2020-06-24' 在 pandas 中 to_datetime 方法可以将字符串解析成多种不同的 Timestamp(时间戳) 对象: 1234567>>> import pandas as pd>>> datestrs = '2011-07-06 12:00:00'>>> type(datestrs)<class 'str'>>>> >>> pd.to_datetime(datestrs)Timestamp('2011-07-06 12:00:00') 基本语法: 1234pandas.to_datetime(arg, errors='raise', dayfirst=False, yearfirst=False, utc=None, format=None, exact=True, unit=None, infer_datetime_format=False, origin='unix', cache=True) 官方文档:https://pandas.pydata.org/docs/reference/api/pandas.to_datetime.html 常用参数: 参数 描述 arg 要转换为日期时间的对象,可以接受 int, float, str, datetime, list, tuple, 1-d array, Series DataFrame/dict-like 类型 errors 如果字符串不满足时间戳的形式,是否会发生异常ignore:不引发异常,返回原始输入;raise:无效解析将引发异常(默认);coerce:无效解析将被设置为NaT dayfirst bool 类型,默认 False,如果 arg 是 str 或列表,是否首先解析为日期例如 dayfirst 为 True,10/11/12 被解析为 2012-11-10,为 False 则解析为 2012-10-11 yearfirst bool 类型,默认 False,如果 arg 是 str 或列表,是否首先解析为年份例如 dayfirst 为 True,10/11/12 被解析为 2010-11-12,为 False 则解析为 2012-10-11如果 dayfirst 和 yearfirst 都为 True,则优先 yearfirst utc bool 类型,是否转换为协调世界时,默认 None format 格式化时间,如 21/2/20 16:10 使用 %d/%m/%y %H:%M 会被解析为 2020-02-21 16:10:00符号含义常见文章:【Python 标准库学习】日期和时间处理库 — datetime 或者官方文档 exact 如果为 True,则需要精确的格式匹配。如果为 False,则允许格式与目标字符串中的任何位置匹配 unit 如果 arg 是整数或浮点数,该参数用于设置其单位(D、s、ms、us、ns) 简单应用: 1234567891011>>> import pandas as pd>>> obj = pd.DataFrame({'year': [2015, 2016], 'month': [2, 3], 'day': [4, 5]})>>> obj year month day0 2015 2 41 2016 3 5>>> >>> pd.to_datetime(obj)0 2015-02-041 2016-03-05dtype: datetime64[ns] 设置 format 和 errors 参数: 1234567891011>>> import pandas as pd>>> pd.to_datetime('13000101', format='%Y%m%d', errors='ignore')datetime.datetime(1300, 1, 1, 0, 0)>>> >>> pd.to_datetime('13000101', format='%Y%m%d', errors='coerce')NaT>>> >>> pd.to_datetime('13000101', format='%Y%m%d', errors='raise')Traceback (most recent call last):...pandas._libs.tslibs.np_datetime.OutOfBoundsDatetime: Out of bounds nanosecond timestamp: 1300-01-01 00:00:00 设置 unit 参数: 123456>>> import pandas as pd>>> pd.to_datetime(1490195805, unit='s')Timestamp('2017-03-22 15:16:45')>>> >>> pd.to_datetime(1490195805433502912, unit='ns')Timestamp('2017-03-22 15:16:45.433502912') 【02x04】date_rangepandas.date_range 方法可用于根据指定的频率生成指定长度的 DatetimeIndex。 基本语法: 123pandas.date_range(start=None, end=None, periods=None, freq=None, tz=None, normalize=False, name=None, closed=None, **kwargs) → pandas.core.indexes.datetimes.DatetimeIndex 官方文档:https://pandas.pydata.org/docs/reference/api/pandas.date_range.html 参数 描述 start 开始日期 end 结束日期 periods int 类型,要生成的时段数(天) freq 频率字符串,即按照某种特定的频率来生成日期,取值参见【02x02】freq 频率部分取值 tz 设置时区,例如 “Asia/Hong_Kong” normalize bool 类型,默认 False,是否在生成日期之前对其进行规范化(仅保留年月日) name 结果 DatetimeIndex 的名称 closed None:默认值,同时保留开始日期和结束日期'left':保留开始日期,不保留结束日期'right':保留结束日期,不保留开始日期 简单示例: 12345>>> import pandas as pd>>> pd.date_range(start='1/1/2018', end='1/08/2018')DatetimeIndex(['2018-01-01', '2018-01-02', '2018-01-03', '2018-01-04', '2018-01-05', '2018-01-06', '2018-01-07', '2018-01-08'], dtype='datetime64[ns]', freq='D') 指定 periods 参数: 1234567891011121314151617181920212223>>> import pandas as pd>>> pd.date_range(start='2012-04-01', periods=20)DatetimeIndex(['2012-04-01', '2012-04-02', '2012-04-03', '2012-04-04', '2012-04-05', '2012-04-06', '2012-04-07', '2012-04-08', '2012-04-09', '2012-04-10', '2012-04-11', '2012-04-12', '2012-04-13', '2012-04-14', '2012-04-15', '2012-04-16', '2012-04-17', '2012-04-18', '2012-04-19', '2012-04-20'], dtype='datetime64[ns]', freq='D')>>> >>> pd.date_range(end='2012-06-01', periods=20)DatetimeIndex(['2012-05-13', '2012-05-14', '2012-05-15', '2012-05-16', '2012-05-17', '2012-05-18', '2012-05-19', '2012-05-20', '2012-05-21', '2012-05-22', '2012-05-23', '2012-05-24', '2012-05-25', '2012-05-26', '2012-05-27', '2012-05-28', '2012-05-29', '2012-05-30', '2012-05-31', '2012-06-01'], dtype='datetime64[ns]', freq='D')>>>>>> pd.date_range(start='2018-04-24', end='2018-04-27', periods=3)DatetimeIndex(['2018-04-24 00:00:00', '2018-04-25 12:00:00', '2018-04-27 00:00:00'], dtype='datetime64[ns]', freq=None)>>>>>> pd.date_range(start='2018-04-24', end='2018-04-28', periods=3)DatetimeIndex(['2018-04-24', '2018-04-26', '2018-04-28'], dtype='datetime64[ns]', freq=None) 指定 freq='M' 会按照每月最后一个日历日的频率生成日期,指定 freq='3M' 会每隔3个月按照每月最后一个日历日的频率生成日期: 1234567891011>>> import pandas as pd>>> pd.date_range(start='1/1/2018', periods=5, freq='M')DatetimeIndex(['2018-01-31', '2018-02-28', '2018-03-31', '2018-04-30', '2018-05-31'], dtype='datetime64[ns]', freq='M')>>> >>> pd.date_range(start='1/1/2018', periods=5, freq='3M')DatetimeIndex(['2018-01-31', '2018-04-30', '2018-07-31', '2018-10-31', '2019-01-31'], dtype='datetime64[ns]', freq='3M')>>> 使用 tz 参数设置时区: 123456789101112>>> import pandas as pd>>> pd.date_range(start='1/1/2018', periods=5, tz='Asia/Tokyo')DatetimeIndex(['2018-01-01 00:00:00+09:00', '2018-01-02 00:00:00+09:00', '2018-01-03 00:00:00+09:00', '2018-01-04 00:00:00+09:00', '2018-01-05 00:00:00+09:00'], dtype='datetime64[ns, Asia/Tokyo]', freq='D')>>> >>> pd.date_range(start='6/24/2020', periods=5, tz='Asia/Hong_Kong')DatetimeIndex(['2020-06-24 00:00:00+08:00', '2020-06-25 00:00:00+08:00', '2020-06-26 00:00:00+08:00', '2020-06-27 00:00:00+08:00', '2020-06-28 00:00:00+08:00'], dtype='datetime64[ns, Asia/Hong_Kong]', freq='D') 设置 normalize 参数,在生成时间戳之前对其进行格式化操作: 12345>>> import pandas as pd>>> pd.date_range('2020-06-24 12:56:31', periods=5, normalize=True)DatetimeIndex(['2020-06-24', '2020-06-25', '2020-06-26', '2020-06-27', '2020-06-28'], dtype='datetime64[ns]', freq='D') 设置 closed 参数: 1234567891011>>> import pandas as pd>>> pd.date_range(start='2020-06-20', end='2020-06-24', closed=None)DatetimeIndex(['2020-06-20', '2020-06-21', '2020-06-22', '2020-06-23', '2020-06-24'], dtype='datetime64[ns]', freq='D')>>> >>> pd.date_range(start='2020-06-20', end='2020-06-24', closed='left')DatetimeIndex(['2020-06-20', '2020-06-21', '2020-06-22', '2020-06-23'], dtype='datetime64[ns]', freq='D')>>> >>> pd.date_range(start='2020-06-20', end='2020-06-24', closed='right')DatetimeIndex(['2020-06-21', '2020-06-22', '2020-06-23', '2020-06-24'], dtype='datetime64[ns]', freq='D') 【02x05】索引与切片Pandas 最基本的时间序列类型就是以时间戳(通常以 Python 字符串或 datatime 对象表示)为索引的Series,这些 datetime 对象实际上是被放在 DatetimeIndex 中的,可以使用类似 pandas.Series 对象的切片方法对其进行索引: 1234567891011121314151617181920212223242526>>> import pandas as pd>>> import numpy as np>>> dates = [datetime(2011, 1, 2), datetime(2011, 1, 5), datetime(2011, 1, 7), datetime(2011, 1, 8), datetime(2011, 1, 10), datetime(2011, 1, 12)]>>> obj = pd.Series(np.random.randn(6), index=dates)>>> >>> obj2011-01-02 -0.4071102011-01-05 -0.1866612011-01-07 -0.7310802011-01-08 0.8609702011-01-10 1.9299732011-01-12 -0.168599dtype: float64>>> >>> obj.indexDatetimeIndex(['2011-01-02', '2011-01-05', '2011-01-07', '2011-01-08', '2011-01-10', '2011-01-12'], dtype='datetime64[ns]', freq=None)>>>>>> obj.index[0]Timestamp('2011-01-02 00:00:00')>>> >>> obj.index[0:3]DatetimeIndex(['2011-01-02', '2011-01-05', '2011-01-07'], dtype='datetime64[ns]', freq=None) 另外还可以传入一个可以被解释为日期的字符串,或者只需传入“年”或“年月”即可轻松选取数据的切片: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172>>> import pandas as pd>>> import numpy as np>>> obj = pd.Series(np.random.randn(1000), index=pd.date_range('1/1/2000', periods=1000))>>> obj2000-01-01 -1.1422842000-01-02 1.1987852000-01-03 2.4669092000-01-04 -0.0867282000-01-05 -0.978437 ... 2002-09-22 -0.2522402002-09-23 0.1485612002-09-24 -1.3304092002-09-25 -0.6734712002-09-26 -0.253271Freq: D, Length: 1000, dtype: float64>>> >>> obj['26/9/2002']-0.25327100684233356>>> >>> obj['2002']2002-01-01 1.0587152002-01-02 0.9008592002-01-03 1.9935082002-01-04 -0.1032112002-01-05 -0.950090 ... 2002-09-22 -0.2522402002-09-23 0.1485612002-09-24 -1.3304092002-09-25 -0.6734712002-09-26 -0.253271Freq: D, Length: 269, dtype: float64>>> >>> obj['2002-09']2002-09-01 -0.9955282002-09-02 0.5015282002-09-03 -0.4867532002-09-04 -1.0839062002-09-05 1.4589752002-09-06 -1.3316852002-09-07 0.1953382002-09-08 -0.4296132002-09-09 1.1258232002-09-10 1.6070512002-09-11 0.5303872002-09-12 -0.0159382002-09-13 1.7810432002-09-14 -0.2771232002-09-15 0.3445692002-09-16 -1.0108102002-09-17 0.4630012002-09-18 1.8836362002-09-19 0.2745202002-09-20 0.6241842002-09-21 -1.2030572002-09-22 -0.2522402002-09-23 0.1485612002-09-24 -1.3304092002-09-25 -0.6734712002-09-26 -0.253271Freq: D, dtype: float64>>> >>> obj['20/9/2002':'26/9/2002']2002-09-20 0.6241842002-09-21 -1.2030572002-09-22 -0.2522402002-09-23 0.1485612002-09-24 -1.3304092002-09-25 -0.6734712002-09-26 -0.253271Freq: D, dtype: float64 【02x06】移动数据与数据偏移移动(shifting)指的是沿着时间轴将数据前移或后移。Series 和 DataFrame 都有一个 shift 方法用于执行单纯的前移或后移操作,保持索引不变: 123456789101112131415161718192021222324>>> import pandas as pd>>> import numpy as np>>> obj = pd.Series(np.random.randn(4), index=pd.date_range('1/1/2000', periods=4, freq='M'))>>> obj2000-01-31 -0.1002172000-02-29 1.1778342000-03-31 -0.6443532000-04-30 -1.954679Freq: M, dtype: float64>>> >>> obj.shift(2)2000-01-31 NaN2000-02-29 NaN2000-03-31 -0.1002172000-04-30 1.177834Freq: M, dtype: float64>>> >>> obj.shift(-2)2000-01-31 -0.6443532000-02-29 -1.9546792000-03-31 NaN2000-04-30 NaNFreq: M, dtype: float64 因为简单的移位操作不会修改索引,所以部分数据会被丢弃并引入 NaN(缺失值)。因此,如果频率已知,则可以将其传给 shift 以便实现对时间戳进行位移而不是对数据进行简单位移: 1234567891011121314151617>>> import pandas as pd>>> import numpy as np>>> obj = pd.Series(np.random.randn(4), index=pd.date_range('1/1/2000', periods=4, freq='M'))>>> obj2000-01-31 -0.1002172000-02-29 1.1778342000-03-31 -0.6443532000-04-30 -1.954679Freq: M, dtype: float64>>> >>> obj.shift(2, freq='M')2000-03-31 -0.1002172000-04-30 1.1778342000-05-31 -0.6443532000-06-30 -1.954679Freq: M, dtype: float64 Pandas 中的频率是由一个基础频率(base frequency)和一个乘数组成的。基础频率通常以一个字符串别名表示,比如 "M" 表示每月,"H" 表示每小时。对于每个基础频率,都有一个被称为日期偏移量(date offset)的对象与之对应。例如,按小时计算的频率可以用 Hour 类表示: 12345678>>> from pandas.tseries.offsets import Hour, Minute>>> hour = Hour()>>> hour<Hour>>>> >>> four_hours = Hour(4)>>> four_hours<4 * Hours> 一般来说,无需明确创建这样的对象,只需使用诸如 "H" 或 "4H" 这样的字符串别名即可。在基础频率前面放上一个整数即可创建倍数: 123456789101112>>> import pandas as pd>>> pd.date_range('2000-01-01', '2000-01-03 23:59', freq='4h')DatetimeIndex(['2000-01-01 00:00:00', '2000-01-01 04:00:00', '2000-01-01 08:00:00', '2000-01-01 12:00:00', '2000-01-01 16:00:00', '2000-01-01 20:00:00', '2000-01-02 00:00:00', '2000-01-02 04:00:00', '2000-01-02 08:00:00', '2000-01-02 12:00:00', '2000-01-02 16:00:00', '2000-01-02 20:00:00', '2000-01-03 00:00:00', '2000-01-03 04:00:00', '2000-01-03 08:00:00', '2000-01-03 12:00:00', '2000-01-03 16:00:00', '2000-01-03 20:00:00'], dtype='datetime64[ns]', freq='4H') 大部分偏移量对象都可通过加法进行连接: 123>>> from pandas.tseries.offsets import Hour, Minute>>> Hour(2) + Minute(30)<150 * Minutes> 对于 freq 参数也可以传入频率字符串(如 "2h30min"),这种字符串可以被高效地解析为等效的表达式: 12345678>>> import pandas as pd>>> pd.date_range('2000-01-01', periods=10, freq='1h30min')DatetimeIndex(['2000-01-01 00:00:00', '2000-01-01 01:30:00', '2000-01-01 03:00:00', '2000-01-01 04:30:00', '2000-01-01 06:00:00', '2000-01-01 07:30:00', '2000-01-01 09:00:00', '2000-01-01 10:30:00', '2000-01-01 12:00:00', '2000-01-01 13:30:00'], dtype='datetime64[ns]', freq='90T') 这种偏移量还可以用在 datetime 或 Timestamp 对象上: 1234>>> from pandas.tseries.offsets import Day, MonthEnd>>> now = datetime(2011, 11, 17)>>> now + 3 * Day()Timestamp('2011-11-20 00:00:00') 如果加的是锚点偏移量,比如 MonthEnd,第一次增量会将原日期向前滚动到符合频率规则的下一个日期: 123456>>> from pandas.tseries.offsets import Day, MonthEnd>>> now = datetime(2011, 11, 17)>>> now + MonthEnd()Timestamp('2011-11-30 00:00:00')>>> now + MonthEnd(2)Timestamp('2011-12-31 00:00:00') 通过锚点偏移量的 rollforward 和 rollback 方法,可明确地将日期向前或向后滚动: 1234567>>> from pandas.tseries.offsets import Day, MonthEnd>>> now = datetime(2011, 11, 17)>>> offset = MonthEnd()>>> offset.rollforward(now)Timestamp('2011-11-30 00:00:00')>>> offset.rollback(now)Timestamp('2011-10-31 00:00:00') 与 groupby 方法结合使用: 12345678910111213141516171819202122232425262728293031323334>>> import pandas as pd>>> import numpy as np>>> from pandas.tseries.offsets import Day, MonthEnd>>> obj = pd.Series(np.random.randn(20), index=pd.date_range('1/15/2000', periods=20, freq='4d'))>>> obj2000-01-15 -0.5917292000-01-19 -0.7758442000-01-23 -0.7456032000-01-27 -0.0764392000-01-31 1.7964172000-02-04 -0.5003492000-02-08 0.5158512000-02-12 -0.3441712000-02-16 0.4196572000-02-20 0.3072882000-02-24 0.1151132000-02-28 -0.3625852000-03-03 1.0748922000-03-07 1.1113662000-03-11 0.9499102000-03-15 -1.5357272000-03-19 0.5459442000-03-23 -0.8101392000-03-27 -1.2606272000-03-31 -0.128403Freq: 4D, dtype: float64>>>>>> offset = MonthEnd()>>> obj.groupby(offset.rollforward).mean()2000-01-31 -0.0786402000-02-29 0.0215432000-03-31 -0.006598dtype: float64 【02x07】时区处理在 Python 中,时区信息来自第三方库 pytz,使用 pytz.common_timezones 方法可以查看所有的时区名称,使用 pytz.timezone 方法从 pytz 中获取时区对象: 1234567>>> import pytz>>> pytz.common_timezones['Africa/Abidjan', 'Africa/Accra', 'Africa/Addis_Ababa', ..., 'UTC']>>>>>> tz = pytz.timezone('Asia/Shanghai')>>> tz<DstTzInfo 'Asia/Shanghai' LMT+8:06:00 STD> # 表示与 UTC 时间相差8小时6分 在 date_range 方法中,tz 参数用于指定时区,默认为 None,可以使用 tz_localize 方法将其进行本地化时区转换,如下示例中,将无时区转本地化 UTC 时区: 12345678910111213141516171819202122232425262728293031>>> import pandas as pd>>> import numpy as np>>> rng = pd.date_range('3/9/2012 9:30', periods=6, freq='D')>>> ts = pd.Series(np.random.randn(len(rng)), index=rng)>>> ts2012-03-09 09:30:00 -1.5279132012-03-10 09:30:00 -1.1161012012-03-11 09:30:00 0.3593582012-03-12 09:30:00 -0.4759202012-03-13 09:30:00 -0.3365702012-03-14 09:30:00 -1.075952Freq: D, dtype: float64>>> >>> print(ts.index.tz)None>>> >>> ts_utc = ts.tz_localize('UTC')>>> ts_utc2012-03-09 09:30:00+00:00 -1.5279132012-03-10 09:30:00+00:00 -1.1161012012-03-11 09:30:00+00:00 0.3593582012-03-12 09:30:00+00:00 -0.4759202012-03-13 09:30:00+00:00 -0.3365702012-03-14 09:30:00+00:00 -1.075952Freq: D, dtype: float64>>>>>> ts_utc.indexDatetimeIndex(['2012-03-09 09:30:00+00:00', '2012-03-10 09:30:00+00:00', '2012-03-11 09:30:00+00:00', '2012-03-12 09:30:00+00:00', '2012-03-13 09:30:00+00:00', '2012-03-14 09:30:00+00:00'], dtype='datetime64[ns, UTC]', freq='D') 时间序列被本地化到某个特定时区后,就可以用 tz_convert 方法将其转换到别的时区了: 123456789101112131415161718192021>>> import pandas as pd>>> import numpy as np>>> rng = pd.date_range('3/9/2012 9:30', periods=6, freq='D')>>> ts = pd.Series(np.random.randn(len(rng)), index=rng)>>> ts2012-03-09 09:30:00 0.4803032012-03-10 09:30:00 -1.4610392012-03-11 09:30:00 -1.5127492012-03-12 09:30:00 -2.1854212012-03-13 09:30:00 1.6578452012-03-14 09:30:00 0.175633Freq: D, dtype: float64>>>>>> ts.tz_localize('UTC').tz_convert('Asia/Shanghai')2012-03-09 17:30:00+08:00 0.4803032012-03-10 17:30:00+08:00 -1.4610392012-03-11 17:30:00+08:00 -1.5127492012-03-12 17:30:00+08:00 -2.1854212012-03-13 17:30:00+08:00 1.6578452012-03-14 17:30:00+08:00 0.175633Freq: D, dtype: float64 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106947061未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【03x00】period 固定时期【03x01】pandas.Period固定时期(period)表示的是时间区间,比如数日、数月、数季、数年等。Period 类所表示的就是这种数据类型,其构造函数需要用到一个字符串或整数。 基本语法: 123class pandas.Period(value=None, freq=None, ordinal=None, year=None, month=None, quarter=None, day=None, hour=None, minute=None, second=None) 官方文档:https://pandas.pydata.org/docs/reference/api/pandas.Period.html 常用参数: 参数 描述 value 时间段 freq 时间戳将具有的偏移量,可以是 str,日期偏移量类型,取值参见【02x02】freq 频率部分取值 以下示例中,Period 对象表示的是从2020年1月1日到2020年12月31日之间的整段时间 123>>> import pandas as pd>>> pd.Period(2020, freq='A-DEC')Period('2020', 'A-DEC') 利用加减法对其按照频率进行位移: 12345678910>>> import pandas as pd>>> obj = pd.Period(2020, freq='A-DEC')>>> objPeriod('2020', 'A-DEC')>>> >>> obj + 5Period('2025', 'A-DEC')>>> >>> obj - 5Period('2015', 'A-DEC') PeriodIndex 类保存了一组 Period,它可以在任何 pandas 数据结构中被用作轴索引: 12345678910111213141516>>> import pandas as pd>>> import numpy as np>>> rng = [pd.Period('2000-01'), pd.Period('2000-02'), pd.Period('2000-03'), pd.Period('2000-04'), pd.Period('2000-05'), pd.Period('2000-06')]>>> obj = pd.Series(np.random.randn(6), index=rng)>>> obj2000-01 0.2290922000-02 1.5154982000-03 -0.3344012000-04 -0.4926812000-05 -2.0128182000-06 0.338804Freq: M, dtype: float64>>> >>> obj.indexPeriodIndex(['2000-01', '2000-02', '2000-03', '2000-04', '2000-05', '2000-06'], dtype='period[M]', freq='M') 123456>>> import pandas as pd>>> values = ['2001Q3', '2002Q2', '2003Q1']>>> index = pd.PeriodIndex(values, freq='Q-DEC')>>> indexPeriodIndex(['2001Q3', '2002Q2', '2003Q1'], dtype='period[Q-DEC]', freq='Q-DEC')>>> 【03x02】period_rangepandas.period_range 方法可根据指定的频率生成指定长度的 PeriodIndex。 基本语法: pandas.period_range(start=None, end=None, periods=None, freq=None, name=None) → pandas.core.indexes.period.PeriodIndex 官方文档:https://pandas.pydata.org/docs/reference/api/pandas.period_range.html 常用参数: 参数 描述 start 起始日期 end 结束日期 periods 要生成的时段数 freq 时间戳将具有的偏移量,可以是 str,日期偏移量类型,取值参见【02x02】freq 频率部分取值 name 结果 PeriodIndex 对象名称 简单应用: 12345678910>>> import pandas as pd>>> pd.period_range(start='2019-01-01', end='2020-01-01', freq='M')PeriodIndex(['2019-01', '2019-02', '2019-03', '2019-04', '2019-05', '2019-06', '2019-07', '2019-08', '2019-09', '2019-10', '2019-11', '2019-12', '2020-01'], dtype='period[M]', freq='M')>>>>>> pd.period_range(start=pd.Period('2017Q1', freq='Q'), end=pd.Period('2017Q2', freq='Q'), freq='M')PeriodIndex(['2017-03', '2017-04', '2017-05', '2017-06'], dtype='period[M]', freq='M') 【03x03】asfreq 时期频率转换Period 和 PeriodIndex 对象都可以通过 asfreq 方法被转换成别的频率。 基本语法:PeriodIndex.asfreq(self, *args, **kwargs) 常用参数: 参数 描述 freq 新的频率(偏移量),取值参见【02x02】freq 频率部分取值 how 按照开始或者结束对齐,'E' or 'END' or 'FINISH';'S' or 'START' or 'BEGIN' 应用示例: 12345678910>>> import pandas as pd>>> pidx = pd.period_range('2010-01-01', '2015-01-01', freq='A')>>> pidxPeriodIndex(['2010', '2011', '2012', '2013', '2014', '2015'], dtype='period[A-DEC]', freq='A-DEC')>>> >>> pidx.asfreq('M')PeriodIndex(['2010-12', '2011-12', '2012-12', '2013-12', '2014-12', '2015-12'], dtype='period[M]', freq='M')>>> >>> pidx.asfreq('M', how='S')PeriodIndex(['2010-01', '2011-01', '2012-01', '2013-01', '2014-01', '2015-01'], dtype='period[M]', freq='M') 【03x04】to_period 与 to_timestamp()to_period 方法可以将 Timestamp(时间戳) 转换为 Period(固定时期); to_timestamp 方法可以将 Period(固定时期)转换为 Timestamp(时间戳) 。 12345678910111213141516171819202122232425262728293031>>> import pandas as pd>>> rng = pd.date_range('2000-01-01', periods=3, freq='M')>>> ts = pd.Series(np.random.randn(3), index=rng)>>> ts2000-01-31 0.2207592000-02-29 -0.1082212000-03-31 0.819433Freq: M, dtype: float64>>> >>> pts = ts.to_period()>>> pts2000-01 0.2207592000-02 -0.1082212000-03 0.819433Freq: M, dtype: float64>>> >>> pts2 = pts.to_timestamp()>>> pts22000-01-01 0.2207592000-02-01 -0.1082212000-03-01 0.819433Freq: MS, dtype: float64>>> >>> ts.indexDatetimeIndex(['2000-01-31', '2000-02-29', '2000-03-31'], dtype='datetime64[ns]', freq='M')>>> >>> pts.indexPeriodIndex(['2000-01', '2000-02', '2000-03'], dtype='period[M]', freq='M')>>> >>> pts2.indexDatetimeIndex(['2000-01-01', '2000-02-01', '2000-03-01'], dtype='datetime64[ns]', freq='MS') 【04x00】timedelta 时间间隔【04x01】pandas.TimedeltaTimedelta 表示持续时间,即两个日期或时间之间的差。 Timedelta 相当于 Python 的 datetime.timedelta,在大多数情况下两者可以互换。 基本语法:class pandas.Timedelta(value=<object object>, unit=None, **kwargs) 官方文档:https://pandas.pydata.org/docs/reference/api/pandas.Timedelta.html 常用参数: 参数 描述 value 传入的值,可以是 Timedelta,timedelta,np.timedelta64,string 或 integer 对象 unit 用于设置 value 的单位,具体取值参见官方文档 表示两个 datetime 对象之间的时间差: 123>>> import pandas as pd>>> pd.to_datetime('2020-6-24') - pd.to_datetime('2016-1-1')Timedelta('1636 days 00:00:00') 通过字符串传递参数: 123>>> import pandas as pd>>> pd.Timedelta('3 days 3 hours 3 minutes 30 seconds')Timedelta('3 days 03:03:30') 通过整数传递参数: 123>>> import pandas as pd>>> pd.Timedelta(5,unit='h')Timedelta('0 days 05:00:00') 获取属性: 123456789>>> import pandas as pd>>> obj = pd.Timedelta('3 days 3 hours 3 minutes 30 seconds')>>> objTimedelta('3 days 03:03:30')>>> >>> obj.days3>>> obj.seconds11010 【04x02】to_timedeltato_timedelta 方法可以将传入的对象转换成 timedelta 对象。 基本语法:pandas.to_timedelta(arg, unit='ns', errors='raise') 官方文档:https://pandas.pydata.org/docs/reference/api/pandas.to_timedelta.html 常用参数: 参数 描述 arg 要转换为 timedelta 的对象,可以是 str,timedelta,list-like 或 Series 对象 unit 用于设置 arg 的单位,具体取值参见官方文档 errors 如果 arg 不满足时间戳的形式,是否会发生异常ignore:不引发异常,返回原始输入;raise:无效解析将引发异常(默认);coerce:无效解析将被设置为NaT 将单个字符串解析为 timedelta 对象: 123456>>> import pandas as pd>>> pd.to_timedelta('1 days 06:05:01.00003')Timedelta('1 days 06:05:01.000030')>>>>>> pd.to_timedelta('15.5us')Timedelta('0 days 00:00:00.000015') 将字符串列表或数组解析为 timedelta 对象: 123>>> import pandas as pd>>> pd.to_timedelta(['1 days 06:05:01.00003', '15.5us', 'nan'])TimedeltaIndex(['1 days 06:05:01.000030', '0 days 00:00:00.000015', NaT], dtype='timedelta64[ns]', freq=None) 指定 unit 参数: 123456>>> import pandas as pd>>> pd.to_timedelta(np.arange(5), unit='s')TimedeltaIndex(['00:00:00', '00:00:01', '00:00:02', '00:00:03', '00:00:04'], dtype='timedelta64[ns]', freq=None)>>> >>> pd.to_timedelta(np.arange(5), unit='d')TimedeltaIndex(['0 days', '1 days', '2 days', '3 days', '4 days'], dtype='timedelta64[ns]', freq=None) 【04x03】timedelta_rangetimedelta_range 方法可根据指定的频率生成指定长度的 TimedeltaIndex。 基本语法: 12pandas.timedelta_range(start=None, end=None, periods=None, freq=None, name=None, closed=None) → pandas.core.indexes.timedeltas.TimedeltaIndex 官方文档:https://pandas.pydata.org/docs/reference/api/pandas.timedelta_range.html 常用参数: 参数 描述 start 开始日期 end 结束日期 periods int 类型,要生成的时段数 freq 频率字符串,即按照某种特定的频率来生成日期,取值参见【02x02】freq 频率部分取值 name 结果 TimedeltaIndex 的名称 closed None:默认值,同时保留开始日期和结束日期'left':保留开始日期,不保留结束日期'right':保留结束日期,不保留开始日期 应用示例: 123>>> import pandas as pd>>> pd.timedelta_range(start='1 day', periods=4)TimedeltaIndex(['1 days', '2 days', '3 days', '4 days'], dtype='timedelta64[ns]', freq='D') closed 参数指定保留哪个端点。默认保留两个端点: 123>>> import pandas as pd>>> pd.timedelta_range(start='1 day', periods=4, closed='right')TimedeltaIndex(['2 days', '3 days', '4 days'], dtype='timedelta64[ns]', freq='D') freq 参数指定 TimedeltaIndex 的频率。只接受固定频率,非固定频率如 'M' 将会报错: 12345678910>>> import pandas as pd>>> pd.timedelta_range(start='1 day', end='2 days', freq='6H')TimedeltaIndex(['1 days 00:00:00', '1 days 06:00:00', '1 days 12:00:00', '1 days 18:00:00', '2 days 00:00:00'], dtype='timedelta64[ns]', freq='6H')>>> >>> pd.timedelta_range(start='1 day', end='2 days', freq='M')Traceback (most recent call last):...ValueError: <MonthEnd> is a non-fixed frequency 【05x00】重采样及频率转换重采样(resampling)指的是将时间序列从一个频率转换到另一个频率的处理过程。将高频率数据聚合到低频率称为降采样(downsampling),而将低频率数据转换到高频率则称为升采样(upsampling)。并不是所有的重采样都能被划分到这两个大类中。例如,将 W-WED(每周三)转换为 W-FRI 既不是降采样也不是升采样。 Pandas 中提供了 resample 方法来帮助我们实现重采样。Pandas 对象都带有一个 resample 方法,它是各种频率转换工作的主力函数。 基本语法: 1234567Series.resample(self, rule, axis=0, closed: Union[str, NoneType] = None, label: Union[str, NoneType] = None, convention: str = 'start', kind: Union[str, NoneType] = None, loffset=None, base: int = 0, on=None, level=None) 1234567DataFrame.resample(self, rule, axis=0, closed: Union[str, NoneType] = None, label: Union[str, NoneType] = None, convention: str = 'start', kind: Union[str, NoneType] = None, loffset=None, base: int = 0, on=None, level=None) 常用参数: 参数 描述 rule axis 重采样的轴,默认 0 closed 在重采样中,各时间段的哪一端是闭合(即包含)的,除 'M'、'A'、'Q'、'BM'、'BA'、'BQ' 和 'W' 默认值为 ‘right’ 外,其他默认值为 ‘left‘ label 在重采样中,如何设置聚合值的标签, right 或 left,默认为 None,例如,9:30 到 9:35 之间的这 5 分钟会被标记为 9:30 或 9:35 convention 仅用于 PeriodIndex(固定时期),对周期进行重采样,'start' or 's','end' or 'e' on 对于 DataFrame 对象,可用该参数指定重采样后的数据的 index(行索引) 为原数据中的某列 level 对于具有层级索引(MultiIndex)的 DataFrame 对象,可以使用该参数来指定需要在哪个级别上进行重新采样 将序列重采样到三分钟的频率,并将每个频率的值相加: 1234567891011121314151617181920>>> import pandas as pd>>> index = pd.date_range('1/1/2000', periods=9, freq='T')>>> series = pd.Series(range(9), index=index)>>> series2000-01-01 00:00:00 02000-01-01 00:01:00 12000-01-01 00:02:00 22000-01-01 00:03:00 32000-01-01 00:04:00 42000-01-01 00:05:00 52000-01-01 00:06:00 62000-01-01 00:07:00 72000-01-01 00:08:00 8Freq: T, dtype: int64>>> >>> series.resample('3T').sum()2000-01-01 00:00:00 32000-01-01 00:03:00 122000-01-01 00:06:00 21Freq: 3T, dtype: int64 设置 label='right',即每个索引 index 会使用靠右侧(较大值)的标签: 1234567891011121314151617181920>>> import pandas as pd>>> index = pd.date_range('1/1/2000', periods=9, freq='T')>>> series = pd.Series(range(9), index=index)>>> series2000-01-01 00:00:00 02000-01-01 00:01:00 12000-01-01 00:02:00 22000-01-01 00:03:00 32000-01-01 00:04:00 42000-01-01 00:05:00 52000-01-01 00:06:00 62000-01-01 00:07:00 72000-01-01 00:08:00 8Freq: T, dtype: int64>>> >>> series.resample('3T', label='right').sum()2000-01-01 00:03:00 32000-01-01 00:06:00 122000-01-01 00:09:00 21Freq: 3T, dtype: int64 设置 closed='right',即结果中会包含原数据中最右侧(较大)的值: 123456789101112131415161718192021>>> import pandas as pd>>> index = pd.date_range('1/1/2000', periods=9, freq='T')>>> series = pd.Series(range(9), index=index)>>> series2000-01-01 00:00:00 02000-01-01 00:01:00 12000-01-01 00:02:00 22000-01-01 00:03:00 32000-01-01 00:04:00 42000-01-01 00:05:00 52000-01-01 00:06:00 62000-01-01 00:07:00 72000-01-01 00:08:00 8Freq: T, dtype: int64>>> >>> series.resample('3T', label='right', closed='right').sum()2000-01-01 00:00:00 02000-01-01 00:03:00 62000-01-01 00:06:00 152000-01-01 00:09:00 15Freq: 3T, dtype: int64 以下示例将序列重采样到30秒的频率,asfreq()[0:5] 用于选择前5行数据: 12345678910111213141516171819202122>>> import pandas as pd>>> index = pd.date_range('1/1/2000', periods=9, freq='T')>>> series = pd.Series(range(9), index=index)>>> series2000-01-01 00:00:00 02000-01-01 00:01:00 12000-01-01 00:02:00 22000-01-01 00:03:00 32000-01-01 00:04:00 42000-01-01 00:05:00 52000-01-01 00:06:00 62000-01-01 00:07:00 72000-01-01 00:08:00 8Freq: T, dtype: int64>>> >>> series.resample('30S').asfreq()[0:5]2000-01-01 00:00:00 0.02000-01-01 00:00:30 NaN2000-01-01 00:01:00 1.02000-01-01 00:01:30 NaN2000-01-01 00:02:00 2.0Freq: 30S, dtype: float64 使用 pad 方法向后填充缺失值(NaN): 12345678910111213141516171819202122>>> import pandas as pd>>> index = pd.date_range('1/1/2000', periods=9, freq='T')>>> series = pd.Series(range(9), index=index)>>> series2000-01-01 00:00:00 02000-01-01 00:01:00 12000-01-01 00:02:00 22000-01-01 00:03:00 32000-01-01 00:04:00 42000-01-01 00:05:00 52000-01-01 00:06:00 62000-01-01 00:07:00 72000-01-01 00:08:00 8Freq: T, dtype: int64>>> >>> series.resample('30S').pad()[0:5]2000-01-01 00:00:00 02000-01-01 00:00:30 02000-01-01 00:01:00 12000-01-01 00:01:30 12000-01-01 00:02:00 2Freq: 30S, dtype: int64 使用 bfill 方法向前填充缺失值(NaN): 12345678910111213141516171819202122>>> import pandas as pd>>> index = pd.date_range('1/1/2000', periods=9, freq='T')>>> series = pd.Series(range(9), index=index)>>> series2000-01-01 00:00:00 02000-01-01 00:01:00 12000-01-01 00:02:00 22000-01-01 00:03:00 32000-01-01 00:04:00 42000-01-01 00:05:00 52000-01-01 00:06:00 62000-01-01 00:07:00 72000-01-01 00:08:00 8Freq: T, dtype: int64>>> >>> series.resample('30S').bfill()[0:5]2000-01-01 00:00:00 02000-01-01 00:00:30 12000-01-01 00:01:00 12000-01-01 00:01:30 22000-01-01 00:02:00 2Freq: 30S, dtype: int64 通过 apply 方法传递自定义函数: 1234567891011121314151617181920212223>>> import pandas as pd>>> index = pd.date_range('1/1/2000', periods=9, freq='T')>>> series = pd.Series(range(9), index=index)>>> series2000-01-01 00:00:00 02000-01-01 00:01:00 12000-01-01 00:02:00 22000-01-01 00:03:00 32000-01-01 00:04:00 42000-01-01 00:05:00 52000-01-01 00:06:00 62000-01-01 00:07:00 72000-01-01 00:08:00 8Freq: T, dtype: int64>>> >>> def custom_resampler(array_like): return np.sum(array_like) + 5>>> series.resample('3T').apply(custom_resampler)2000-01-01 00:00:00 82000-01-01 00:03:00 172000-01-01 00:06:00 26Freq: 3T, dtype: int64 convention 参数的应用: 12345678910111213141516171819202122232425>>> import pandas as pd>>> s = pd.Series([1, 2], index=pd.period_range('2012-01-01', freq='A', periods=2))>>> s2012 12013 2Freq: A-DEC, dtype: int64>>> >>> s.resample('Q', convention='start').asfreq()2012Q1 1.02012Q2 NaN2012Q3 NaN2012Q4 NaN2013Q1 2.02013Q2 NaN2013Q3 NaN2013Q4 NaNFreq: Q-DEC, dtype: float64>>> >>> s.resample('Q', convention='end').asfreq()2012Q4 1.02013Q1 NaN2013Q2 NaN2013Q3 NaN2013Q4 2.0Freq: Q-DEC, dtype: float64 123456789101112131415161718192021222324252627282930313233343536>>> import pandas as pd>>> q = pd.Series([1, 2, 3, 4], index=pd.period_range('2018-01-01', freq='Q', periods=4))>>> q2018Q1 12018Q2 22018Q3 32018Q4 4Freq: Q-DEC, dtype: int64>>> >>> q.resample('M', convention='end').asfreq()2018-03 1.02018-04 NaN2018-05 NaN2018-06 2.02018-07 NaN2018-08 NaN2018-09 3.02018-10 NaN2018-11 NaN2018-12 4.0Freq: M, dtype: float64>>> >>> q.resample('M', convention='start').asfreq()2018-01 1.02018-02 NaN2018-03 NaN2018-04 2.02018-05 NaN2018-06 NaN2018-07 3.02018-08 NaN2018-09 NaN2018-10 4.02018-11 NaN2018-12 NaNFreq: M, dtype: float64 对于 DataFrame 对象,可以使用关键字 on 来指定原数据中的某列为重采样后数据的行索引: 123456789101112131415161718192021>>> import pandas as pd>>> d = dict({'price': [10, 11, 9, 13, 14, 18, 17, 19], 'volume': [50, 60, 40, 100, 50, 100, 40, 50]})>>> df = pd.DataFrame(d)>>> df['week_starting'] = pd.date_range('01/01/2018', periods=8, freq='W')>>> df price volume week_starting0 10 50 2018-01-071 11 60 2018-01-142 9 40 2018-01-213 13 100 2018-01-284 14 50 2018-02-045 18 100 2018-02-116 17 40 2018-02-187 19 50 2018-02-25>>> >>> df.resample('M', on='week_starting').mean() price volumeweek_starting 2018-01-31 10.75 62.52018-02-28 17.00 60.0 对于具有层级索引(MultiIndex)的 DataFrame 对象,可以使用关键字 level 来指定需要在哪个级别上进行重新采样: 12345678910111213141516171819202122>>> import pandas as pd>>> days = pd.date_range('1/1/2000', periods=4, freq='D')>>> d2 = dict({'price': [10, 11, 9, 13, 14, 18, 17, 19], 'volume': [50, 60, 40, 100, 50, 100, 40, 50]})>>> df2 = pd.DataFrame(d2, index=pd.MultiIndex.from_product([days, ['morning', 'afternoon']]))>>> df2 price volume2000-01-01 morning 10 50 afternoon 11 602000-01-02 morning 9 40 afternoon 13 1002000-01-03 morning 14 50 afternoon 18 1002000-01-04 morning 17 40 afternoon 19 50>>> >>> df2.resample('D', level=0).sum() price volume2000-01-01 21 1102000-01-02 22 1402000-01-03 32 1502000-01-04 36 90 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106947061未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Pandas","slug":"Python-数据分析/Pandas","permalink":"https://www.itrhx.com/categories/Python-数据分析/Pandas/"}],"tags":[{"name":"Pandas","slug":"Pandas","permalink":"https://www.itrhx.com/tags/Pandas/"},{"name":"时间序列","slug":"时间序列","permalink":"https://www.itrhx.com/tags/时间序列/"}]},{"title":"Python 数据分析三剑客之 Pandas(八):数据重塑/重复数据处理/数据替换","slug":"A86-Pandas-08","date":"2020-06-22T13:01:54.429Z","updated":"2020-07-06T13:45:25.429Z","comments":true,"path":"2020/06/22/A86-Pandas-08/","link":"","permalink":"https://www.itrhx.com/2020/06/22/A86-Pandas-08/","excerpt":"","text":"Pandas 系列文章: Python 数据分析三剑客之 Pandas(一):认识 Pandas 及其 Series、DataFrame 对象 Python 数据分析三剑客之 Pandas(二):Index 索引对象以及各种索引操作 Python 数据分析三剑客之 Pandas(三):算术运算与缺失值的处理 Python 数据分析三剑客之 Pandas(四):函数应用、映射、排序和层级索引 Python 数据分析三剑客之 Pandas(五):统计计算与统计描述 Python 数据分析三剑客之 Pandas(六):GroupBy 数据分裂、应用与合并 Python 数据分析三剑客之 Pandas(七):合并数据集 Python 数据分析三剑客之 Pandas(八):数据重塑、重复数据处理与数据替换 Python 数据分析三剑客之 Pandas(九):时间序列 Python 数据分析三剑客之 Pandas(十):数据读写 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106900748未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【01x00】数据重塑有许多用于重新排列表格型数据的基础运算。这些函数也称作重塑(reshape)或轴向旋转(pivot)运算。重塑层次化索引主要有以下两个方法: stack:将数据的列转换成行; unstack:将数据的行转换成列。 【01x01】stackstack 方法用于将数据的列转换成为行; 基本语法:DataFrame.stack(self, level=-1, dropna=True) 官方文档:https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.stack.html 参数 描述 level 从列转换到行,指定不同层级的列索引或列标签、由列索引或列标签组成的数组,默认-1 dropna bool 类型,是否删除重塑后数据中所有值为 NaN 的行,默认 True 单层列(Single level columns): 12345678910111213>>> import pandas as pd>>> obj = pd.DataFrame([[0, 1], [2, 3]], index=['cat', 'dog'], columns=['weight', 'height'])>>> obj weight heightcat 0 1dog 2 3>>> >>> obj.stack()cat weight 0 height 1dog weight 2 height 3dtype: int64 多层列(Multi level columns): 123456789101112131415>>> import pandas as pd>>> multicol = pd.MultiIndex.from_tuples([('weight', 'kg'), ('weight', 'pounds')])>>> obj = pd.DataFrame([[1, 2], [2, 4]], index=['cat', 'dog'], columns=multicol)>>> obj weight kg poundscat 1 2dog 2 4>>> >>> obj.stack() weightcat kg 1 pounds 2dog kg 2 pounds 4 缺失值填充: 123456789101112131415>>> import pandas as pd>>> multicol = pd.MultiIndex.from_tuples([('weight', 'kg'), ('height', 'm')])>>> obj = pd.DataFrame([[1.0, 2.0], [3.0, 4.0]], index=['cat', 'dog'], columns=multicol)>>> obj weight height kg mcat 1.0 2.0dog 3.0 4.0>>> >>> obj.stack() height weightcat kg NaN 1.0 m 2.0 NaNdog kg NaN 3.0 m 4.0 NaN 通过 level 参数指定不同层级的轴进行重塑: 1234567891011121314151617181920212223242526272829>>> import pandas as pd>>> multicol = pd.MultiIndex.from_tuples([('weight', 'kg'), ('height', 'm')])>>> obj = pd.DataFrame([[1.0, 2.0], [3.0, 4.0]], index=['cat', 'dog'], columns=multicol)>>> obj weight height kg mcat 1.0 2.0dog 3.0 4.0>>> >>> obj.stack(level=0) kg mcat height NaN 2.0 weight 1.0 NaNdog height NaN 4.0 weight 3.0 NaN>>> >>> obj.stack(level=1) height weightcat kg NaN 1.0 m 2.0 NaNdog kg NaN 3.0 m 4.0 NaN>>>>>> obj.stack(level=[0, 1])cat height m 2.0 weight kg 1.0dog height m 4.0 weight kg 3.0dtype: float64 对于重塑后的数据,若有一行的值均为 NaN,则默认会被删除,可以设置 dropna=False 来保留缺失值: 123456789101112131415161718192021>>> import pandas as pd>>> multicol = pd.MultiIndex.from_tuples([('weight', 'kg'), ('height', 'm')])>>> obj = pd.DataFrame([[None, 1.0], [2.0, 3.0]], index=['cat', 'dog'], columns=multicol)>>> obj weight height kg mcat NaN 1.0dog 2.0 3.0>>> >>> obj.stack(dropna=False) height weightcat kg NaN NaN m 1.0 NaNdog kg NaN 2.0 m 3.0 NaN>>> >>> obj.stack(dropna=True) height weightcat m 1.0 NaNdog kg NaN 2.0 m 3.0 NaN 【01x02】unstackunstack:将数据的行转换成列。 基本语法: Series.unstack(self, level=-1, fill_value=None) DataFrame.unstack(self, level=-1, fill_value=None) 官方文档: https://pandas.pydata.org/docs/reference/api/pandas.Series.unstack.html https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.unstack.html 参数 描述 level 从行转换到列,指定不同层级的行索引,默认-1 fill_value 用于替换 NaN 的值 在 Series 对象中的应用: 123456789101112131415161718>>> import pandas as pd>>> obj = pd.Series([1, 2, 3, 4], index=pd.MultiIndex.from_product([['one', 'two'], ['a', 'b']]))>>> objone a 1 b 2two a 3 b 4dtype: int64>>> >>> obj.unstack() a bone 1 2two 3 4>>> >>> obj.unstack(level=0) one twoa 1 3b 2 4 和 stack 方法类似,如果值不存在将会引入缺失值(NaN): 123456789101112131415161718>>> import pandas as pd>>> obj1 = pd.Series([0, 1, 2, 3], index=['a', 'b', 'c', 'd'])>>> obj2 = pd.Series([4, 5, 6], index=['c', 'd', 'e'])>>> obj3 = pd.concat([obj1, obj2], keys=['one', 'two'])>>> obj3one a 0 b 1 c 2 d 3two c 4 d 5 e 6dtype: int64>>> >>> obj3.unstack() a b c d eone 0.0 1.0 2.0 3.0 NaNtwo NaN NaN 4.0 5.0 6.0 在 DataFrame 对象中的应用: 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame(np.arange(6).reshape((2, 3)), index=pd.Index(['Ohio','Colorado'], name='state'), columns=pd.Index(['one', 'two', 'three'], name='number'))>>> objnumber one two threestate Ohio 0 1 2Colorado 3 4 5>>> >>> obj2 = obj.stack()>>> obj2state numberOhio one 0 two 1 three 2Colorado one 3 two 4 three 5dtype: int32>>> >>> obj3 = pd.DataFrame({'left': obj2, 'right': obj2 + 5}, columns=pd.Index(['left', 'right'], name='side'))>>> obj3side left rightstate number Ohio one 0 5 two 1 6 three 2 7Colorado one 3 8 two 4 9 three 5 10>>> >>> obj3.unstack('state')side left right state Ohio Colorado Ohio Coloradonumber one 0 3 5 8two 1 4 6 9three 2 5 7 10>>> >>> obj3.unstack('state').stack('side')state Colorado Ohionumber side one left 3 0 right 8 5two left 4 1 right 9 6three left 5 2 right 10 7 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106900748未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【02x00】重复数据处理 duplicated:判断是否为重复值; drop_duplicates:删除重复值。 【02x01】duplicatedduplicated 方法可以判断值是否为重复数据。 基本语法: Series.duplicated(self, keep='first') DataFrame.duplicated(self, subset: Union[Hashable, Sequence[Hashable], NoneType] = None, keep: Union[str, bool] = 'first') → ’Series’ 官方文档: https://pandas.pydata.org/docs/reference/api/pandas.Series.duplicated.html https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.duplicated.html 参数 描述 keep 标记重复项的方法,默认 'first''first':将非重复项和第一个重复项标记为 False,其他重复项标记为 True'last':将非重复项和最后一个重复项标记为 False,其他重复项标记为 TrueFalse:将所有重复项标记为 True,非重复项标记为 False subset 列标签或标签序列,在 DataFrame 对象中才有此参数,用于指定某列,仅标记该列的重复项,默认情况下将考虑所有列 默认情况下,对于每组重复的值,第一个出现的重复值标记为 False,其他重复项标记为 True,非重复项标记为 False,相当于 keep='first': 12345678910111213141516171819202122232425>>> import pandas as pd>>> obj = pd.Series(['lama', 'cow', 'lama', 'beetle', 'lama'])>>> obj0 lama1 cow2 lama3 beetle4 lamadtype: object>>> >>> obj.duplicated()0 False1 False2 True3 False4 Truedtype: bool>>>>>> obj.duplicated(keep='first')0 False1 False2 True3 False4 Truedtype: bool 设置 keep='last',将每组非重复项和最后一次出现的重复项标记为 False,其他重复项标记为 True,设置 keep=False,则所有重复项均为 True,其他值为 False: 12345678910111213141516171819202122232425>>> import pandas as pd>>> obj = pd.Series(['lama', 'cow', 'lama', 'beetle', 'lama'])>>> obj0 lama1 cow2 lama3 beetle4 lamadtype: object>>> >>> obj.duplicated(keep='last')0 True1 False2 True3 False4 Falsedtype: bool>>> >>> obj.duplicated(keep=False)0 True1 False2 True3 False4 Truedtype: bool 在 DataFrame 对象中,subset 参数用于指定某列,仅标记该列的重复项,默认情况下将考虑所有列: 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame({'data1' : ['a'] * 4 + ['b'] * 4, 'data2' : np.random.randint(0, 4, 8)})>>> obj data1 data20 a 01 a 02 a 03 a 34 b 35 b 36 b 07 b 2>>> >>> obj.duplicated()0 False1 True2 True3 False4 False5 True6 False7 Falsedtype: bool>>> >>> obj.duplicated(subset='data1')0 False1 True2 True3 True4 False5 True6 True7 Truedtype: bool>>> >>> obj.duplicated(subset='data2', keep='last')0 True1 True2 True3 True4 True5 False6 False7 Falsedtype: bool 【02x02】drop_duplicatesdrop_duplicates 方法会返回一个删除了重复值的序列。 基本语法: 1Series.drop_duplicates(self, keep='first', inplace=False) 12345DataFrame.drop_duplicates(self, subset: Union[Hashable, Sequence[Hashable], NoneType] = None, keep: Union[str, bool] = 'first', inplace: bool = False, ignore_index: bool = False) → Union[ForwardRef(‘DataFrame’), NoneType] 官方文档: https://pandas.pydata.org/docs/reference/api/pandas.Series.drop_duplicates.html https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.drop_duplicates.html 参数 描述 keep 删除重复项的方法,默认 'first''first':保留非重复项和第一个重复项,其他重复项标记均删除'last':保留非重复项和最后一个重复项,其他重复项删除False:将所有重复项删除,非重复项保留 inplace 是否返回删除重复项后的值,默认 False,若设置为 True,则不返回值,直接改变原数据 subset 列标签或标签序列,在 DataFrame 对象中才有此参数,用于指定某列,仅标记该列的重复项,默认情况下将考虑所有列 ignore_index bool 类型,在 DataFrame 对象中才有此参数,是否忽略原对象的轴标记,默认 False,如果为 True,则新对象的索引将是 0, 1, 2, …, n-1 keep 参数的使用: 123456789101112131415161718192021222324252627282930>>> import pandas as pd>>> obj = pd.Series(['lama', 'cow', 'lama', 'beetle', 'lama', 'hippo'], name='animal')>>> obj0 lama1 cow2 lama3 beetle4 lama5 hippoName: animal, dtype: object>>> >>> obj.drop_duplicates()0 lama1 cow3 beetle5 hippoName: animal, dtype: object>>> >>> obj.drop_duplicates(keep='last')1 cow3 beetle4 lama5 hippoName: animal, dtype: object>>> >>> obj.drop_duplicates(keep=False)1 cow3 beetle5 hippoName: animal, dtype: object 如果设置 inplace=True,则不会返回任何值,但原对象的值已被改变: 12345678910111213141516171819202122232425262728>>> import pandas as pd>>> obj1 = pd.Series(['lama', 'cow', 'lama', 'beetle', 'lama', 'hippo'], name='animal')>>> obj10 lama1 cow2 lama3 beetle4 lama5 hippoName: animal, dtype: object>>> >>> obj2 = obj1.drop_duplicates()>>> obj2 # 有返回值0 lama1 cow3 beetle5 hippoName: animal, dtype: object>>> >>> obj3 = obj1.drop_duplicates(inplace=True)>>> obj3 # 无返回值>>>>>> obj1 # 原对象的值已改变0 lama1 cow3 beetle5 hippoName: animal, dtype: object 在 DataFrame 对象中的使用: 12345678910111213141516171819202122232425262728293031323334>>> import numpy as np>>> import pandas as pd>>> obj = pd.DataFrame({'data1' : ['a'] * 4 + ['b'] * 4, 'data2' : np.random.randint(0, 4, 8)})>>> obj data1 data20 a 21 a 12 a 13 a 24 b 15 b 26 b 07 b 0>>> >>> obj.drop_duplicates() data1 data20 a 21 a 14 b 15 b 26 b 0>>> >>> obj.drop_duplicates(subset='data2') data1 data20 a 21 a 16 b 0>>> >>> obj.drop_duplicates(subset='data2', ignore_index=True) data1 data20 a 21 a 12 b 0 【03x00】数据替换【03x01】replacereplace 方法可以根据值的内容进行替换。 基本语法: Series.replace(self, to_replace=None, value=None, inplace=False, limit=None, regex=False, method='pad') DataFrame.replace(self, to_replace=None, value=None, inplace=False, limit=None, regex=False, method='pad') 官方文档: https://pandas.pydata.org/docs/reference/api/pandas.Series.replace.html https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.replace.html 常用参数: 参数 描述 to_replace 找到要替换值的方法,可以是:字符串、正则表达式、列表、字典、整数、浮点数、Series 对象或者 None使用不同参数的区别参见官方文档 value 用于替换匹配项的值, 对于 DataFrame,可以使用字典的值来指定每列要使用的值,还允许使用此类对象的正则表达式,字符串和列表或字典 inplace bool 类型,是否直接改变原数据且不返回值,默认 False regex bool 类型或者与 to_replace 相同的类型,当 to_replace 参数为正则表达式时,regex 应为 True,或者直接使用该参数代替 to_replace to_replace 和 value 参数只传入一个值,单个值替换单个值: 1234567891011121314151617>>> import pandas as pd>>> obj = pd.Series([0, 1, 2, 3, 4])>>> obj0 01 12 23 34 4dtype: int64>>> >>> obj.replace(0, 5)0 51 12 23 34 4dtype: int64 to_replace 传入多个值,value 传入一个值,多个值替换一个值: 1234567891011121314151617>>> import pandas as pd>>> obj = pd.Series([0, 1, 2, 3, 4])>>> obj0 01 12 23 34 4dtype: int64>>> >>> obj.replace([0, 1, 2, 3], 4)0 41 42 43 44 4dtype: int64 to_replace 和 value 参数都传入多个值,多个值替换多个值: 1234567891011121314151617>>> import pandas as pd>>> obj = pd.Series([0, 1, 2, 3, 4])>>> obj0 01 12 23 34 4dtype: int64>>> >>> obj.replace([0, 1, 2, 3], [4, 3, 2, 1])0 41 32 23 14 4dtype: int64 to_replace 传入字典: 123456789101112131415161718192021222324252627282930313233343536373839404142>>> import pandas as pd>>> obj = pd.DataFrame({'A': [0, 1, 2, 3, 4], 'B': [5, 6, 7, 8, 9], 'C': ['a', 'b', 'c', 'd', 'e']})>>> obj A B C0 0 5 a1 1 6 b2 2 7 c3 3 8 d4 4 9 e>>> >>> obj.replace(0, 5) A B C0 5 5 a1 1 6 b2 2 7 c3 3 8 d4 4 9 e>>> >>> obj.replace({0: 10, 1: 100}) A B C0 10 5 a1 100 6 b2 2 7 c3 3 8 d4 4 9 e>>> >>> obj.replace({'A': 0, 'B': 5}, 100) A B C0 100 100 a1 1 6 b2 2 7 c3 3 8 d4 4 9 e>>> obj.replace({'A': {0: 100, 4: 400}}) A B C0 100 5 a1 1 6 b2 2 7 c3 3 8 d4 400 9 e to_replace 传入正则表达式: 1234567891011121314151617181920212223242526272829303132333435363738>>> import pandas as pd>>> obj = pd.DataFrame({'A': ['bat', 'foo', 'bait'], 'B': ['abc', 'bar', 'xyz']})>>> obj A B0 bat abc1 foo bar2 bait xyz>>> >>> obj.replace(to_replace=r'^ba.$', value='new', regex=True) A B0 new abc1 foo new2 bait xyz>>> >>> obj.replace({'A': r'^ba.$'}, {'A': 'new'}, regex=True) A B0 new abc1 foo bar2 bait xyz>>> >>> obj.replace(regex=r'^ba.$', value='new') A B0 new abc1 foo new2 bait xyz>>> >>> obj.replace(regex={r'^ba.$': 'new', 'foo': 'xyz'}) A B0 new abc1 xyz new2 bait xyz>>> >>> obj.replace(regex=[r'^ba.$', 'foo'], value='new') A B0 new abc1 new new2 bait xyz 【03x02】wherewhere 方法用于替换条件为 False 的值。 基本语法: Series.where(self, cond, other=nan, inplace=False, axis=None, level=None, errors='raise', try_cast=False) DataFrame.where(self, cond, other=nan, inplace=False, axis=None, level=None, errors='raise', try_cast=False) 官方文档: https://pandas.pydata.org/docs/reference/api/pandas.Series.where.html https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.where.html 常用参数: 参数 描述 cond 替换条件,如果 cond 为 True,则保留原始值。如果为 False,则替换为来自 other 的相应值 other 替换值,如果 cond 为 False,则替换为来自该参数的相应值 inplace bool 类型,是否直接改变原数据且不返回值,默认 False 在 Series 中的应用: 12345678910111213141516171819202122232425>>> import pandas as pd>>> obj = pd.Series(range(5))>>> obj0 01 12 23 34 4dtype: int64>>> >>> obj.where(obj > 0)0 NaN1 1.02 2.03 3.04 4.0dtype: float64>>> >>> obj.where(obj > 1, 10)0 101 102 23 34 4dtype: int64 在 DataFrame 中的应用: 1234567891011121314151617181920212223242526>>> import pandas as pd>>> obj = pd.DataFrame(np.arange(10).reshape(-1, 2), columns=['A', 'B'])>>> obj A B0 0 11 2 32 4 53 6 74 8 9>>> >>> m = obj % 3 == 0>>> obj.where(m, -obj) A B0 0 -11 -2 32 -4 -53 6 -74 -8 9>>> >>> obj.where(m, -obj) == np.where(m, obj, -obj) A B0 True True1 True True2 True True3 True True4 True True 【03x03】maskmask 方法与 where 方法相反,mask 用于替换条件为 False 的值。 基本语法: Series.mask(self, cond, other=nan, inplace=False, axis=None, level=None, errors='raise', try_cast=False) DataFrame.mask(self, cond, other=nan, inplace=False, axis=None, level=None, errors='raise', try_cast=False) 官方文档: https://pandas.pydata.org/docs/reference/api/pandas.Series.mask.html https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.mask.html 常用参数: 参数 描述 cond 替换条件,如果 cond 为 False,则保留原始值。如果为 True,则替换为来自 other 的相应值 other 替换值,如果 cond 为 False,则替换为来自该参数的相应值 inplace bool 类型,是否直接改变原数据且不返回值,默认 False 在 Series 中的应用: 12345678910111213141516171819202122232425>>> import pandas as pd>>> obj = pd.Series(range(5))>>> obj0 01 12 23 34 4dtype: int64>>> >>> obj.mask(obj > 0)0 0.01 NaN2 NaN3 NaN4 NaNdtype: float64>>> >>> obj.mask(obj > 1, 10)0 01 12 103 104 10dtype: int64 在 DataFrame 中的应用: 123456789101112131415161718192021222324252627>>> import pandas as pd>>> obj = pd.DataFrame(np.arange(10).reshape(-1, 2), columns=['A', 'B'])>>> obj A B0 0 11 2 32 4 53 6 74 8 9>>> >>> m = obj % 3 == 0>>> >>> obj.mask(m, -obj) A B0 0 11 2 -32 4 53 -6 74 8 -9>>> >>> obj.where(m, -obj) == obj.mask(~m, -obj) A B0 True True1 True True2 True True3 True True4 True True 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106900748未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Pandas","slug":"Python-数据分析/Pandas","permalink":"https://www.itrhx.com/categories/Python-数据分析/Pandas/"}],"tags":[{"name":"Pandas","slug":"Pandas","permalink":"https://www.itrhx.com/tags/Pandas/"},{"name":"数据重塑","slug":"数据重塑","permalink":"https://www.itrhx.com/tags/数据重塑/"},{"name":"数据替换","slug":"数据替换","permalink":"https://www.itrhx.com/tags/数据替换/"}]},{"title":"Python 数据分析三剑客之 Pandas(七):合并数据集","slug":"A85-Pandas-07","date":"2020-06-21T13:04:21.174Z","updated":"2020-07-06T13:45:11.851Z","comments":true,"path":"2020/06/21/A85-Pandas-07/","link":"","permalink":"https://www.itrhx.com/2020/06/21/A85-Pandas-07/","excerpt":"","text":"Pandas 系列文章: Python 数据分析三剑客之 Pandas(一):认识 Pandas 及其 Series、DataFrame 对象 Python 数据分析三剑客之 Pandas(二):Index 索引对象以及各种索引操作 Python 数据分析三剑客之 Pandas(三):算术运算与缺失值的处理 Python 数据分析三剑客之 Pandas(四):函数应用、映射、排序和层级索引 Python 数据分析三剑客之 Pandas(五):统计计算与统计描述 Python 数据分析三剑客之 Pandas(六):GroupBy 数据分裂、应用与合并 Python 数据分析三剑客之 Pandas(七):合并数据集 Python 数据分析三剑客之 Pandas(八):数据重塑、重复数据处理与数据替换 Python 数据分析三剑客之 Pandas(九):时间序列 Python 数据分析三剑客之 Pandas(十):数据读写 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106830112未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【01x00】concatpandas.concat 可以沿着指定轴将多个对象堆叠到一起。 官方文档:https://pandas.pydata.org/docs/reference/api/pandas.concat.html 基本语法: 12345678910pandas.concat(objs: Union[Iterable[‘DataFrame’], Mapping[Optional[Hashable], ‘DataFrame’]], axis='0', join: str = \"'outer'\", ignore_index: bool = 'False', keys='None', levels='None', names='None', verify_integrity: bool = 'False', sort: bool = 'False', copy: bool = 'True') → ’DataFrame’ 12345678910pandas.concat(objs: Union[Iterable[FrameOrSeriesUnion], Mapping[Optional[Hashable], FrameOrSeriesUnion]], axis='0', join: str = \"'outer'\", ignore_index: bool = 'False', keys='None', levels='None', names='None', verify_integrity: bool = 'False', sort: bool = 'False', copy: bool = 'True') → FrameOrSeriesUnion 常用参数描述: 参数 描述 objs Series 或 DataFrame 对象的序列或映射,要合并的对象 axis 沿指定轴合并,0 or ‘index’,1 or ‘columns’,只有在 DataFrame 中才有 1 or 'columns’ join 如何处理其他轴(或多个轴)上的索引,可取值:‘inner’,‘outer’(默认值)‘outer’:当 axis = 0 时,列名相同的列会合并,其余列都保留(并集),空值填充;‘inner’:当 axis = 0 时,列名相同的列会合并,其余列都舍弃(交集) ignore_index bool 类型,连接后的值是否使用原索引值,如果为 True,则索引将会是 0, 1, …, n-1 keys 序列形式,默认 None,传递 keys 后,会构造一个层次索引,即 MultiIndex 对象,keys 为最外层索引 levels 用于构造 MultiIndex 的特定级别(唯一值)。未指定则将从键中推断出来 names 列表类型,为索引添加标签 verify_integrity bool 类型,是否检查合并后的索引有无重复项,设置为 True 若有重复项则会报错 sort 当 join='outer' 时对列索引进行排序。当 join='inner' 时此操作无效 合并两个 Series 对象: 123456789>>> import pandas as pd>>> obj1 = pd.Series(['a', 'b'])>>> obj2 = pd.Series(['c', 'd'])>>> pd.concat([obj1, obj2])0 a1 b0 c1 ddtype: object 设置 ignore_index=True,放弃原有的索引值: 123456789>>> import pandas as pd>>> obj1 = pd.Series(['a', 'b'])>>> obj2 = pd.Series(['c', 'd'])>>> pd.concat([obj1, obj2], ignore_index=True)0 a1 b2 c3 ddtype: object 设置 keys 参数,添加最外层的索引: 123456789>>> import pandas as pd>>> obj1 = pd.Series(['a', 'b'])>>> obj2 = pd.Series(['c', 'd'])>>> pd.concat([obj1, obj2], keys=['s1', 's2'])s1 0 a 1 bs2 0 c 1 ddtype: object 设置 names 参数,为索引添加标签: 12345678910>>> import pandas as pd>>> obj1 = pd.Series(['a', 'b'])>>> obj2 = pd.Series(['c', 'd'])>>> pd.concat([obj1, obj2], keys=['s1', 's2'], names=['Series name', 'Row ID'])Series name Row IDs1 0 a 1 bs2 0 c 1 ddtype: object 合并 DataFrame 对象: 12345678910111213141516171819>>> import pandas as pd>>> obj1 = pd.DataFrame([['a', 1], ['b', 2]], columns=['letter', 'number'])>>> obj2 = pd.DataFrame([['c', 3], ['d', 4]], columns=['letter', 'number'])>>> obj1 letter number0 a 11 b 2>>> >>> obj2 letter number0 c 31 d 4>>> >>> pd.concat([obj1, obj2]) letter number0 a 11 b 20 c 31 d 4 合并 DataFrame 对象,不存在的值将会被 NaN 填充: 12345678910111213141516171819>>> import pandas as pd>>> obj1 = pd.DataFrame([['a', 1], ['b', 2]], columns=['letter', 'number'])>>> obj2 = pd.DataFrame([['c', 3, 'cat'], ['d', 4, 'dog']], columns=['letter', 'number', 'animal'])>>> obj1 letter number0 a 11 b 2>>> >>> obj2 letter number animal0 c 3 cat1 d 4 dog>>> >>> pd.concat([obj1, obj2]) letter number animal0 a 1 NaN1 b 2 NaN0 c 3 cat1 d 4 dog 合并 DataFrame 对象,设置 join="inner" 不存在的列将会舍弃: 12345678910111213141516171819>>> import pandas as pd>>> obj1 = pd.DataFrame([['a', 1], ['b', 2]], columns=['letter', 'number'])>>> obj2 = pd.DataFrame([['c', 3, 'cat'], ['d', 4, 'dog']], columns=['letter', 'number', 'animal'])>>> obj1 letter number0 a 11 b 2>>> >>> obj2 letter number animal0 c 3 cat1 d 4 dog>>> >>> pd.concat([obj1, obj2], join=\"inner\") letter number0 a 11 b 20 c 31 d 4 合并 DataFrame 对象,设置 axis=1 沿 y 轴合并(增加列): 1234567891011121314151617>>> import pandas as pd>>> obj1 = pd.DataFrame([['a', 1], ['b', 2]], columns=['letter', 'number'])>>> obj2 = pd.DataFrame([['bird', 'polly'], ['monkey', 'george']], columns=['animal', 'name'])>>> obj1 letter number0 a 11 b 2>>> >>> obj2 animal name0 bird polly1 monkey george>>> >>> pd.concat([obj1, obj2], axis=1) letter number animal name0 a 1 bird polly1 b 2 monkey george 设置 verify_integrity=True ,检查新的索引是否有重复项,有重复项会报错: 123456789101112131415>>> import pandas as pd>>> obj1 = pd.DataFrame([1], index=['a'])>>> obj2 = pd.DataFrame([2], index=['a'])>>> obj1 0a 1>>> >>> obj2 0a 2>>> >>> pd.concat([obj1, obj2], verify_integrity=True)Traceback (most recent call last): ...ValueError: Indexes have overlapping values: ['a'] 设置 sort=True,会对列索引进行排序输出: 123456789101112131415161718>>> obj1 = pd.DataFrame([['a', 3], ['d', 2]], columns=['letter', 'number'])>>> obj2 = pd.DataFrame([['c', 1, 'cat'], ['b', 4, 'dog']], columns=['letter', 'number', 'animal'])>>> obj1 letter number0 a 31 d 2>>> >>> obj2 letter number animal0 c 1 cat1 b 4 dog>>> >>> pd.concat([obj1, obj2], sort=True) animal letter number0 NaN a 31 NaN d 20 cat c 11 dog b 4 【02x00】appendAppend 方法事实上是在一个 Series / DataFrame 对象后最追加另一个 Series / DataFrame 对象并返回一个新对象,不改变原对象的值。 基本语法: Series.append(self, to_append, ignore_index=False, verify_integrity=False) DataFrame.append(self, other, ignore_index=False, verify_integrity=False, sort=False) 官方文档: https://pandas.pydata.org/docs/reference/api/pandas.Series.append.html https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.append.html 参数描述: 参数 描述 to_append / other 要追加的数据 ignore_index bool 类型,连接后的值是否使用原索引值,如果为 True,则索引将会是 0, 1, …, n-1 verify_integrity bool 类型,是否检查合并后的索引有无重复项,设置为 True 若有重复项则会报错 sort bool 类型,是否对列索引(columns)进行排序,默认 False 合并 Series 对象: 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253>>> import pandas as pd>>> obj1 = pd.Series([1, 2, 3])>>> obj2 = pd.Series([4, 5, 6])>>> obj3 = pd.Series([4, 5, 6], index=[3, 4, 5])>>> obj10 11 22 3dtype: int64>>> >>> obj20 41 52 6dtype: int64>>> >>> obj33 44 55 6dtype: int64>>> >>> obj1.append(obj2)0 11 22 30 41 52 6dtype: int64>>> >>> obj1.append(obj3)0 11 22 33 44 55 6dtype: int64>>> >>> obj1.append(obj2, ignore_index=True)0 11 22 33 44 55 6dtype: int64>>> >>> obj1.append(obj2, verify_integrity=True)Traceback (most recent call last):...ValueError: Indexes have overlapping values: Int64Index([0, 1, 2], dtype='int64') 合并 DataFrame 对象: 123456789101112131415161718192021222324252627>>> import pandas as pd>>> obj1 = pd.DataFrame([[1, 2], [3, 4]], columns=list('AB'))>>> obj2 = pd.DataFrame([[5, 6], [7, 8]], columns=list('AB'))>>> >>> obj1 A B0 1 21 3 4>>> >>> obj2 A B0 5 61 7 8>>> >>> obj1.append(obj2) A B0 1 21 3 40 5 61 7 8>>> >>> obj1.append(obj2, ignore_index=True) A B0 1 21 3 42 5 63 7 8 以下虽然不是生成 DataFrames 的推荐方法,但演示了从多个数据源生成 DataFrames 的两种方法: 12345678910111213>>> import pandas as pd>>> obj = pd.DataFrame(columns=['A'])>>> for i in range(5): obj = obj.append({'A': i}, ignore_index=True) >>> obj A0 01 12 23 34 4 12345678>>> import pandas as pd>>> pd.concat([pd.DataFrame([i], columns=['A']) for i in range(5)], ignore_index=True) A0 01 12 23 34 4 【03x00】merge将不同的数据源进行合并是数据科学中常见的操作,这既包括将两个不同的数据集非常简单地拼接在一起,也包括用数据库那样的连接(join)与合并(merge)操作处理有重叠字段的数据集。Series 与DataFrame 都具备这类操作,Pandas 的函数与方法让数据合并变得快速简单。 数据集的合并(merge)或连接(join)运算是通过一个或多个键将行连接起来的。这些运算是关系型数据库(基于SQL)的核心。Pandas 的 merge 函数是对数据应用这些算法的主要切入点。 pandas.merge 可根据一个或多个连接键将不同 DataFrame 中的行连接起来。 基本语法: 12345678910111213pandas.merge(left, right, how: str = 'inner', on=None, left_on=None, right_on=None, left_index: bool = False, right_index: bool = False, sort: bool = False, suffixes='_x', '_y', copy: bool = True, indicator: bool = False, validate=None) → ’DataFrame’ 官方文档:https://pandas.pydata.org/docs/reference/api/pandas.merge.html 常见参数描述: 参数 描述 left 参与合并的左侧 DataFrame 对象 right 参与合并的右侧 DataFrame 对象 how 合并方式,默认 'inner''inner':内连接,即使用两个对象中都有的键(交集);'outer':外连接,即使用两个对象中所有的键(并集);'left':左连接,即使用左对象中所有的键;'right':右连接,即使用右对象中所有的键; on 用于连接的列名。必须存在于左右两个 Dataframe对象中如果未指定,且其他连接键也未指定,则以 left 和 right 列名的交集作为连接键 left_on 左侧 DataFrame 对象中用作连接键的列 right_on 右侧 DataFrame 对象中用作连接键的列 left_index bool 类型,是否使用左侧 DataFrame 对象中的索引(index)作为连接键,默认 False right_index bool 类型,是否使用右侧 DataFrame 对象中的索引(index)作为连接键,默认 False sort bool 类型,是否在结果中按顺序对连接键排序,默认 False。如果为 False,则连接键的顺序取决于联接类型(how 关键字) suffixes 字符串值元组,用于追加到重叠列名的末尾,默认为 ('_x', '_y')。例如,如果左右两个 DataFrame 对象都有 data 列时,则结果中就会出现 data_x 和 data_y 【03x01】一对一连接一对一连接是指两个 DataFrame 对象的列的值没有重复值。 如果不指定任何参数,调用 merge 方法,merge 就会将重叠的列的列名当做键来合并。 在下面的示例中,两个 DataFrame 对象都有一个列名为 key 的列,未指定按照哪一列来合并,merge 就会默认按照 key 来合并: 1234567891011121314151617181920>>> import pandas as pd>>> obj1 = pd.DataFrame({'key': ['b', 'a', 'c'], 'data1': range(3)})>>> obj2 = pd.DataFrame({'key': ['a', 'c', 'b'], 'data2': range(3)})>>> obj1 key data10 b 01 a 12 c 2>>> >>> obj2 key data20 a 01 c 12 b 2>>> >>> pd.merge(obj1, obj2) key data1 data20 b 0 21 a 1 02 c 2 1 【03x02】多对一连接多对一连接是指两个 DataFrame 对象中,有一个的列的值有重复值。通过多对一连接获得的结果,DataFrame 将会保留重复值。12345678910111213141516171819202122232425262728>>> import pandas as pd>>> obj1 = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'a', 'b'], 'data1': range(7)})>>> obj2 = pd.DataFrame({'key': ['a', 'b', 'd'], 'data2': range(3)})>>> >>> obj1 key data10 b 01 b 12 a 23 c 34 a 45 a 56 b 6>>> >>> obj2 key data20 a 01 b 12 d 2>>> >>> pd.merge(obj1, obj2) key data1 data20 b 0 11 b 1 12 b 6 13 a 2 04 a 4 05 a 5 0### 【03x03】多对多连接 多对多连接是指两个 DataFrame 对象中的列的值都有重复值。 1234567891011121314151617181920212223242526272829>>> import pandas as pd>>> obj1 = pd.DataFrame({'key': ['a', 'b', 'b', 'c'], 'data1': range(4)})>>> obj2 = pd.DataFrame({'key': ['a', 'a', 'b', 'b', 'c', 'c'], 'data2': range(6)})>>> obj1 key data10 a 01 b 12 b 23 c 3>>> >>> obj2 key data20 a 01 a 12 b 23 b 34 c 45 c 5>>> >>> pd.merge(obj1, obj2) key data1 data20 a 0 01 a 0 12 b 1 23 b 1 34 b 2 25 b 2 36 c 3 47 c 3 5 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106830112未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【03x04】参数 on / left_on / right_on参数 on 用于指定按照某一列来进行合并,若不指定该参数,则会默认按照重叠的列的列名当做键来合并: 1234567891011121314151617181920>>> import pandas as pd>>> obj1 = pd.DataFrame({'key': ['b', 'a', 'c'], 'data1': range(3)})>>> obj2 = pd.DataFrame({'key': ['a', 'c', 'b'], 'data2': range(3)})>>> obj1 key data10 b 01 a 12 c 2>>> >>> obj2 key data20 a 01 c 12 b 2>>> >>> pd.merge(obj1, obj2, on='key') key data1 data20 b 0 21 a 1 02 c 2 1 如果要根据多个键进行合并,传入一个由列名组成的列表即可: 12345678910111213141516171819202122232425>>> import pandas as pd>>> left = pd.DataFrame({'key1': ['foo', 'foo', 'bar'], 'key2': ['one', 'two', 'one'], 'lval': [1, 2, 3]})>>> right = pd.DataFrame({'key1': ['foo', 'foo', 'bar', 'bar'], 'key2': ['one', 'one', 'one', 'two'], 'rval': [4, 5, 6, 7]})>>> left key1 key2 lval0 foo one 11 foo two 22 bar one 3>>> >>> right key1 key2 rval0 foo one 41 foo one 52 bar one 63 bar two 7>>> >>> pd.merge(left, right, on=['key1', 'key2']) key1 key2 lval rval0 foo one 1 41 foo one 1 52 bar one 3 6 如果两个对象的列名不同,就可以使用 left_on、right_on 参数分别进行指定: 123456789101112131415161718192021222324252627>>> import pandas as pd>>> obj1 = pd.DataFrame({'lkey': ['b', 'b', 'a', 'c', 'a', 'a', 'b'], 'data1': range(7)})>>> obj2 = pd.DataFrame({'rkey': ['a', 'b', 'd'], 'data2': range(3)})>>> obj1 lkey data10 b 01 b 12 a 23 c 34 a 45 a 56 b 6>>> >>> obj2 rkey data20 a 01 b 12 d 2>>> >>> pd.merge(obj1, obj2, left_on='lkey', right_on='rkey') lkey data1 rkey data20 b 0 b 11 b 1 b 12 b 6 b 13 a 2 a 04 a 4 a 05 a 5 a 0 【03x05】参数 how在前面的示例中,结果里面 c 和 d 以及与之相关的数据消失了。默认情况下,merge 做的是内连接('inner'),结果中的键是交集。其他方式还有:'left'、'right'、'outer',含义如下: 'inner':内连接,即使用两个对象中都有的键(交集); 'outer':外连接,即使用两个对象中所有的键(并集); 'left':左连接,即使用左对象中所有的键; 'right':右连接,即使用右对象中所有的键; 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758>>> import pandas as pd>>> obj1 = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'a', 'b'], 'data1': range(7)})>>> obj2 = pd.DataFrame({'key': ['a', 'b', 'd'], 'data2': range(3)})>>> obj1 key data10 b 01 b 12 a 23 c 34 a 45 a 56 b 6>>> >>> obj2 key data20 a 01 b 12 d 2>>> >>> pd.merge(obj1, obj2, on='key', how='inner') key data1 data20 b 0 11 b 1 12 b 6 13 a 2 04 a 4 05 a 5 0>>> >>> pd.merge(obj1, obj2, on='key', how='outer') key data1 data20 b 0.0 1.01 b 1.0 1.02 b 6.0 1.03 a 2.0 0.04 a 4.0 0.05 a 5.0 0.06 c 3.0 NaN7 d NaN 2.0>>> >>> pd.merge(obj1, obj2, on='key', how='left') key data1 data20 b 0 1.01 b 1 1.02 a 2 0.03 c 3 NaN4 a 4 0.05 a 5 0.06 b 6 1.0>>> >>> pd.merge(obj1, obj2, on='key', how='right') key data1 data20 b 0.0 11 b 1.0 12 b 6.0 13 a 2.0 04 a 4.0 05 a 5.0 06 d NaN 2 【03x06】参数 suffixessuffixes 参数用于指定附加到左右两个 DataFrame 对象的重叠列名上的字符串: 在以下示例中,选择按照 key1 进行合并,而两个 DataFrame 对象都包含 key2 列,如果未指定 suffixes 参数,则默认会为两个对象的 key2 加上 _x 和 _y,以便区分它们,如果指定了 suffixes 参数,就会按照添加指定的后缀: 12345678910111213141516171819202122232425262728293031323334353637>>> import pandas as pd>>> left = pd.DataFrame({'key1': ['foo', 'foo', 'bar'], 'key2': ['one', 'two', 'one'], 'lval': [1, 2, 3]})>>> right = pd.DataFrame({'key1': ['foo', 'foo', 'bar', 'bar'], 'key2': ['one', 'one', 'one', 'two'], 'rval': [4, 5, 6, 7]})>>> left key1 key2 lval0 foo one 11 foo two 22 bar one 3>>> >>> right key1 key2 rval0 foo one 41 foo one 52 bar one 63 bar two 7>>> >>> pd.merge(left, right, on='key1') key1 key2_x lval key2_y rval0 foo one 1 one 41 foo one 1 one 52 foo two 2 one 43 foo two 2 one 54 bar one 3 one 65 bar one 3 two 7>>> >>> pd.merge(left, right, on='key1', suffixes=('_left', '_right')) key1 key2_left lval key2_right rval0 foo one 1 one 41 foo one 1 one 52 foo two 2 one 43 foo two 2 one 54 bar one 3 one 65 bar one 3 two 7 【03x07】参数 left_index / right_index有时候,DataFrame 中的连接键位于其索引中。在这种情况下,可以使用 left_index=True 或right_index=True(或两个都传)以说明索引应该被用作连接键。这种方法称为按索引连接,在 Pandas 中还有个 join 方法可以实现这个功能。 在以下示例中,按照 left 的 key 列进行连接,而 right 对象的连接键位于其索引中,因此要指定 right_index=True: 123456789101112131415161718192021222324>>> import pandas as pd>>> left = pd.DataFrame({'key': ['a', 'b', 'a', 'a', 'b', 'c'], 'value': range(6)})>>> right = pd.DataFrame({'group_val': [3.5, 7]}, index=['a', 'b'])>>> left key value0 a 01 b 12 a 23 a 34 b 45 c 5>>> >>> right group_vala 3.5b 7.0>>> >>> pd.merge(left, right, left_on='key', right_index=True) key value group_val0 a 0 3.52 a 2 3.53 a 3 3.51 b 1 7.04 b 4 7.0 【04x00】joinjoin 方法只适用于 DataFrame 对象,Series 对象没有该方法,该方法用于连接另一个 DataFrame 对象的列(columns)。 基本语法:DataFrame.join(self, other, on=None, how='left', lsuffix='', rsuffix='', sort=False) → ’DataFrame’ 参数描述: 参数 描述 other 另一个 DataFrame、Series 或 DataFrame 列表对象 on 列名称,或者列名称组成的列表、元组,连接的列 how 合并方式,默认 'left''inner':内连接,即使用两个对象中都有的键(交集);'outer':外连接,即使用两个对象中所有的键(并集);'left':左连接,即使用左对象中所有的键;'right':右连接,即使用右对象中所有的键; lsuffix 当两个对象有相同的列名时,合并后左边数据列名的后缀 rsuffix 当两个对象有相同的列名时,合并后右边数据列名的后缀 sort bool 类型,是否在结果中按顺序对连接键排序,默认 False。如果为 False,则连接键的顺序取决于联接类型(how 关键字) 使用 lsuffix 和 rsuffix 参数: 12345678910111213141516171819202122232425262728>>> import pandas as pd>>> obj = pd.DataFrame({'key': ['K0', 'K1', 'K2', 'K3', 'K4', 'K5'], 'A': ['A0', 'A1', 'A2', 'A3', 'A4', 'A5']})>>> other = pd.DataFrame({'key': ['K0', 'K1', 'K2'], 'B': ['B0', 'B1', 'B2']})>>> obj key A0 K0 A01 K1 A12 K2 A23 K3 A34 K4 A45 K5 A5>>> >>> other key B0 K0 B01 K1 B12 K2 B2>>> >>> obj.join(other, lsuffix='_1', rsuffix='_2') key_1 A key_2 B0 K0 A0 K0 B01 K1 A1 K1 B12 K2 A2 K2 B23 K3 A3 NaN NaN4 K4 A4 NaN NaN5 K5 A5 NaN NaN 如果右表的索引是左表的某一列的值,这时可以将右表的索引和左表的列对齐合并这样的灵活方式进行合并: 123456789101112131415161718192021>>> import pandas as pd>>> obj = pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'], 'B': ['B0', 'B1', 'B2', 'B3'],'key': ['K0', 'K1', 'K0', 'K1']})>>> other = pd.DataFrame({'C': ['C0', 'C1'],'D': ['D0', 'D1']},index=['K0', 'K1'])>>> obj A B key0 A0 B0 K01 A1 B1 K12 A2 B2 K03 A3 B3 K1>>> >>> other C DK0 C0 D0K1 C1 D1>>> >>> obj.join(other, on='key') A B key C D0 A0 B0 K0 C0 D01 A1 B1 K1 C1 D12 A2 B2 K0 C0 D03 A3 B3 K1 C1 D1 【05x00】四种方法的区别 concat:可用于两个或多个 Series 或 DataFrame 对象间,通过 axis 参数指定按照行方向(增加行)或列方向(增加列)进合并操作,默认行合并(增加行),取并集; append:在一个 Series 或 DataFrame 对象后最追加另一个 Series 或 DataFrame 对象并返回一个新对象,不改变原对象的值。只能按行合并(增加行)。 merge:只能对两个 DataFrame 对象进行合并,一般按照列方向(增加列)进行合并操作,按照行方向合并一般用 join 方法代替,默认列合并(增加列),取交集; join:只能对两个 DataFrame 对象进行合并,按照列方向(增加列)进行合并操作,默认左连接。 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106830112未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Pandas","slug":"Python-数据分析/Pandas","permalink":"https://www.itrhx.com/categories/Python-数据分析/Pandas/"}],"tags":[{"name":"Pandas","slug":"Pandas","permalink":"https://www.itrhx.com/tags/Pandas/"},{"name":"合并数据集","slug":"合并数据集","permalink":"https://www.itrhx.com/tags/合并数据集/"}]},{"title":"Python 数据分析三剑客之 Pandas(六):GroupBy 数据分裂/应用/合并","slug":"A84-Pandas-06","date":"2020-06-17T15:58:49.204Z","updated":"2020-08-07T01:13:45.027Z","comments":true,"path":"2020/06/17/A84-Pandas-06/","link":"","permalink":"https://www.itrhx.com/2020/06/17/A84-Pandas-06/","excerpt":"","text":"Pandas 系列文章: Python 数据分析三剑客之 Pandas(一):认识 Pandas 及其 Series、DataFrame 对象 Python 数据分析三剑客之 Pandas(二):Index 索引对象以及各种索引操作 Python 数据分析三剑客之 Pandas(三):算术运算与缺失值的处理 Python 数据分析三剑客之 Pandas(四):函数应用、映射、排序和层级索引 Python 数据分析三剑客之 Pandas(五):统计计算与统计描述 Python 数据分析三剑客之 Pandas(六):GroupBy 数据分裂、应用与合并 Python 数据分析三剑客之 Pandas(七):合并数据集 Python 数据分析三剑客之 Pandas(八):数据重塑、重复数据处理与数据替换 Python 数据分析三剑客之 Pandas(九):时间序列 Python 数据分析三剑客之 Pandas(十):数据读写 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106804881未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【01x00】GroupBy 机制对数据集进行分组并对各组应用一个函数(无论是聚合还是转换),通常是数据分析工作中的重要环节。在将数据集加载、融合、准备好之后,通常就是计算分组统计或生成透视表。Pandas 提供了一个灵活高效的 GroupBy 功能,虽然“分组”(group by)这个名字是借用 SQL 数据库语言的命令,但其理念引用发明 R 语言 frame 的 Hadley Wickham 的观点可能更合适:分裂(Split)、应用(Apply)和组合(Combine)。 分组运算过程:Split —> Apply —> Combine 分裂(Split):根据某些标准将数据分组; 应用(Apply):对每个组独立应用一个函数; 合并(Combine):把每个分组的计算结果合并起来。 官方介绍:https://pandas.pydata.org/docs/user_guide/groupby.html 【02x00】GroupBy 对象常见的 GroupBy 对象:Series.groupby、DataFrame.groupby,基本语法如下: 123456789Series.groupby(self, by=None, axis=0, level=None, as_index: bool = True, sort: bool = True, group_keys: bool = True, squeeze: bool = False, observed: bool = False) → ’groupby_generic.SeriesGroupBy’ 123456789DataFrame.groupby(self, by=None, axis=0, level=None, as_index: bool = True, sort: bool = True, group_keys: bool = True, squeeze: bool = False, observed: bool = False) → ’groupby_generic.DataFrameGroupBy’ 官方文档: https://pandas.pydata.org/docs/reference/api/pandas.Series.groupby.html https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.groupby.html 常用参数解释如下: 参数 描述 by 映射、函数、标签或标签列表,用于确定分组依据的分组。如果 by 是函数,则会在对象索引的每个值上调用它。 如果传递了 dict 或 Series,则将使用 Series 或 dict 的值来确定组(将 Series 的值首先对齐;请参见.align() 方法)。 如果传递了 ndarray,则按原样使用这些值来确定组。标签或标签列表可以按自身中的列传递给分组。 注意,元组被解释为(单个)键 axis 沿指定轴拆分,默认 0,0 or ‘index’,1 or ‘columns’,只有在 DataFrame 中才有 1 or 'columns’ level 如果轴是 MultiIndex(层次结构),则按特定层级进行分组,默认 None as_index bool 类型,默认 True,对于聚合输出,返回以组标签为索引的对象。仅与 DataFrame 输入相关。as_index=False 实际上是“SQL样式”分组输出 sort bool 类型,默认 True,对组键排序。关闭此选项可获得更好的性能。注:这不影响每组的观察顺序。Groupby 保留每个组中行的顺序 group_keys bool 类型,默认 True,调用 apply 方法时,是否将组键(keys)添加到索引( index)以标识块 squeeze bool 类型,默认 False,如果可能,减少返回类型的维度,否则返回一致的类型 groupby() 进行分组,GroupBy 对象没有进行实际运算,只是包含分组的中间数据,示例如下: 123456789101112131415161718192021222324>>> import pandas as pd>>> import numpy as np>>> data = {'key1' : ['a', 'b', 'a', 'b', 'a', 'b', 'a', 'a'], 'key2' : ['one', 'one', 'two', 'three', 'two', 'two', 'one', 'three'], 'data1': np.random.randn(8), 'data2': np.random.randn(8)}>>> >>> obj = pd.DataFrame(data)>>> obj key1 key2 data1 data20 a one -0.804160 -0.8689051 b one -0.086990 0.3257412 a two 0.757992 0.5411013 b three -0.281435 0.0978414 a two 0.817757 -0.6436995 b two -0.462760 -0.3211966 a one -0.403699 0.6021387 a three 0.883940 -0.850526>>> >>> obj.groupby('key1')<pandas.core.groupby.generic.DataFrameGroupBy object at 0x03CDB7C0>>>> >>> obj['data1'].groupby(obj['key1'])<pandas.core.groupby.generic.SeriesGroupBy object at 0x03CDB748> 【03x00】GroupBy Split 数据分裂【03x01】分组运算前面通过 groupby() 方法获得了一个 GroupBy 对象,它实际上还没有进行任何计算,只是含有一些有关分组键 obj['key1'] 的中间数据而已。换句话说,该对象已经有了接下来对各分组执行运算所需的一切信息。例如,我们可以调用 GroupBy 的 mean() 方法来计算分组平均值,size() 方法返回每个分组的元素个数: 123456789101112131415161718192021222324252627282930313233343536373839404142434445>>> import pandas as pd>>> import numpy as np>>> data = {'key1' : ['a', 'b', 'a', 'b', 'a', 'b', 'a', 'a'], 'key2' : ['one', 'one', 'two', 'three', 'two', 'two', 'one', 'three'], 'data1': np.random.randn(8), 'data2': np.random.randn(8)}>>> >>> obj = pd.DataFrame(data)>>> obj key1 key2 data1 data20 a one -0.544099 -0.6140791 b one 2.193712 0.1010052 a two -0.004683 0.8827703 b three 0.312858 1.7321054 a two 0.011089 0.0895875 b two 0.292165 1.3276386 a one -1.433291 -0.2389717 a three -0.004724 -2.117326>>> >>> grouped1 = obj.groupby('key1')>>> grouped2 = obj['data1'].groupby(obj['key1'])>>> >>> grouped1.mean() data1 data2key1 a -0.395142 -0.399604b 0.932912 1.053583>>> >>> grouped2.mean()key1a -0.395142b 0.932912Name: data1, dtype: float64>>>>>> grouped1.size()key1a 5b 3dtype: int64>>> >>> grouped2.size()key1a 5b 3Name: data1, dtype: int64 【03x02】按类型按列分组groupby() 方法 axis 参数默认是 0,通过设置也可以在其他任何轴上进行分组,也支持按照类型(dtype)进行分组: 12345678910111213141516171819202122232425262728293031323334353637383940>>> import pandas as pd>>> import numpy as np>>> data = {'key1' : ['a', 'b', 'a', 'b', 'a', 'b', 'a', 'a'], 'key2' : ['one', 'one', 'two', 'three', 'two', 'two', 'one', 'three'], 'data1': np.random.randn(8), 'data2': np.random.randn(8)}>>> obj = pd.DataFrame(data)>>> obj key1 key2 data1 data20 a one -0.607009 1.9483011 b one 0.150818 -0.0250952 a two -2.086024 0.3581643 b three 0.446061 1.7087974 a two 0.745457 -0.9809485 b two 0.981877 2.1593276 a one 0.804480 -0.4996617 a three 0.112884 0.004367>>> >>> obj.dtypeskey1 objectkey2 objectdata1 float64data2 float64dtype: object>>> >>> obj.groupby(obj.dtypes, axis=1).size()float64 2object 2dtype: int64>>> >>> obj.groupby(obj.dtypes, axis=1).sum() float64 object0 1.341291 aone1 0.125723 bone2 -1.727860 atwo3 2.154858 bthree4 -0.235491 atwo5 3.141203 btwo6 0.304819 aone7 0.117251 athree 【03x03】自定义分组groupby() 方法中可以一次传入多个数组的列表,也可以自定义一组分组键。也可以通过一个字典、一个函数,或者按照索引层级进行分组。 传入多个数组的列表: 12345678910111213141516171819202122232425262728293031323334>>> import pandas as pd>>> import numpy as np>>> data = {'key1' : ['a', 'b', 'a', 'b', 'a', 'b', 'a', 'a'], 'key2' : ['one', 'one', 'two', 'three', 'two', 'two', 'one', 'three'], 'data1': np.random.randn(8), 'data2': np.random.randn(8)}>>> obj = pd.DataFrame(data)>>> obj key1 key2 data1 data20 a one -0.841652 0.6880551 b one 0.510042 -0.5611712 a two -0.418862 -0.1459833 b three -1.104698 0.5631584 a two 0.329527 -0.8931085 b two 0.753653 -0.3425206 a one -0.882527 -1.1213297 a three 1.726794 0.160244>>> >>> means = obj['data1'].groupby([obj['key1'], obj['key2']]).mean()>>> meanskey1 key2 a one -0.862090 three 1.726794 two -0.044667b one 0.510042 three -1.104698 two 0.753653Name: data1, dtype: float64>>> >>> means.unstack()key2 one three twokey1 a -0.862090 1.726794 -0.044667b 0.510042 -1.104698 0.753653 自定义分组键: 1234567891011121314151617181920212223>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame({'key1' : ['a', 'a', 'b', 'b', 'a'], 'key2' : ['one', 'two', 'one', 'two', 'one'], 'data1' : np.random.randn(5), 'data2' : np.random.randn(5)})>>> obj key1 key2 data1 data20 a one -0.024003 0.3504801 a two -0.767534 -0.1004262 b one -0.594983 -1.9455803 b two -0.374482 0.8175924 a one 0.755452 -0.137759>>> >>> states = np.array(['Wuhan', 'Beijing', 'Beijing', 'Wuhan', 'Wuhan'])>>> years = np.array([2005, 2005, 2006, 2005, 2006])>>> >>> obj['data1'].groupby([states, years]).mean()Beijing 2005 -0.767534 2006 -0.594983Wuhan 2005 -0.199242 2006 0.755452Name: data1, dtype: float64 【03x03x01】字典分组通过字典进行分组: 1234567891011121314151617181920212223242526272829303132333435>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame(np.random.randint(1, 10, (5,5)), columns=['a', 'b', 'c', 'd', 'e'], index=['A', 'B', 'C', 'D', 'E'])>>> obj a b c d eA 1 4 7 1 9B 8 2 4 7 8C 9 8 2 5 1D 2 4 2 8 3E 7 5 7 2 3>>> >>> obj_dict = {'a':'Python', 'b':'Python', 'c':'Java', 'd':'C++', 'e':'Java'}>>> obj.groupby(obj_dict, axis=1).size()C++ 1Java 2Python 2dtype: int64>>> >>> obj.groupby(obj_dict, axis=1).count() C++ Java PythonA 1 2 2B 1 2 2C 1 2 2D 1 2 2E 1 2 2>>> >>> obj.groupby(obj_dict, axis=1).sum() C++ Java PythonA 1 16 5B 7 12 10C 5 3 17D 8 5 6E 2 10 12 【03x03x02】函数分组通过函数进行分组: 123456789101112131415161718192021222324>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame(np.random.randint(1, 10, (5,5)), columns=['a', 'b', 'c', 'd', 'e'], index=['AA', 'BBB', 'CC', 'D', 'EE'])>>> obj a b c d eAA 3 9 5 8 2BBB 1 4 2 2 6CC 9 2 4 7 6D 2 5 5 7 1EE 8 8 8 2 2>>> >>> def group_key(idx): \"\"\" idx 为列索引或行索引 \"\"\" return len(idx)>>> obj.groupby(group_key).size() # 等价于 obj.groupby(len).size()1 12 33 1dtype: int64 【03x03x03】索引层级分组通过不同索引层级进行分组: 1234567891011121314151617181920212223242526272829>>> import pandas as pd>>> import numpy as np>>> columns = pd.MultiIndex.from_arrays([['Python', 'Java', 'Python', 'Java', 'Python'], ['A', 'A', 'B', 'C', 'B']], names=['language', 'index'])>>> obj = pd.DataFrame(np.random.randint(1, 10, (5, 5)), columns=columns)>>> objlanguage Python Java Python Java Pythonindex A A B C B0 7 1 9 8 51 4 5 4 5 62 4 3 1 9 53 6 6 3 8 14 7 9 2 8 2>>> >>> obj.groupby(level='language', axis=1).sum()language Java Python0 9 211 10 142 12 103 14 104 17 11>>> >>> obj.groupby(level='index', axis=1).sum()index A B C0 8 14 81 9 10 52 7 6 93 12 4 84 16 4 8 【03x04】分组迭代GroupBy 对象支持迭代,对于单层分组,可以产生一组二元元组,由分组名和数据块组成: 1234567891011121314151617181920212223242526272829303132333435>>> import pandas as pd>>> import numpy as np>>> data = {'key1' : ['a', 'b', 'a', 'b', 'a', 'b', 'a', 'a'], 'key2' : ['one', 'one', 'two', 'three', 'two', 'two', 'one', 'three'], 'data1': np.random.randn(8), 'data2': np.random.randn(8)}>>> obj = pd.DataFrame(data)>>> obj key1 key2 data1 data20 a one -1.088762 0.6685041 b one 0.275500 0.7878442 a two -0.108417 -0.4912963 b three 0.019524 -0.3633904 a two 0.453612 0.7969995 b two 1.982858 1.5018776 a one 1.101132 -1.9283627 a three 0.524775 -1.205842>>> >>> for group_name, group_data in obj.groupby('key1'): print(group_name) print(group_data) a key1 key2 data1 data20 a one -1.088762 0.6685042 a two -0.108417 -0.4912964 a two 0.453612 0.7969996 a one 1.101132 -1.9283627 a three 0.524775 -1.205842b key1 key2 data1 data21 b one 0.275500 0.7878443 b three 0.019524 -0.3633905 b two 1.982858 1.501877 对于多层分组,元组的第一个元素将会是由键值组成的元组,第二个元素为数据块: 12345678910111213141516171819202122232425262728293031323334353637383940414243>>> import pandas as pd>>> import numpy as np>>> data = {'key1' : ['a', 'b', 'a', 'b', 'a', 'b', 'a', 'a'], 'key2' : ['one', 'one', 'two', 'three', 'two', 'two', 'one', 'three'], 'data1': np.random.randn(8), 'data2': np.random.randn(8)}>>> obj = pd.DataFrame(data)>>> obj key1 key2 data1 data20 a one -1.088762 0.6685041 b one 0.275500 0.7878442 a two -0.108417 -0.4912963 b three 0.019524 -0.3633904 a two 0.453612 0.7969995 b two 1.982858 1.5018776 a one 1.101132 -1.9283627 a three 0.524775 -1.205842>>> >>> for group_name, group_data in obj.groupby(['key1', 'key2']): print(group_name) print(group_data) ('a', 'one') key1 key2 data1 data20 a one -1.088762 0.6685046 a one 1.101132 -1.928362('a', 'three') key1 key2 data1 data27 a three 0.524775 -1.205842('a', 'two') key1 key2 data1 data22 a two -0.108417 -0.4912964 a two 0.453612 0.796999('b', 'one') key1 key2 data1 data21 b one 0.2755 0.787844('b', 'three') key1 key2 data1 data23 b three 0.019524 -0.36339('b', 'two') key1 key2 data1 data25 b two 1.982858 1.501877 【03x05】对象转换GroupBy 对象支持转换成列表或字典: 123456789101112131415161718192021222324252627282930313233343536373839404142>>> import pandas as pd>>> import numpy as np>>> data = {'key1' : ['a', 'b', 'a', 'b', 'a', 'b', 'a', 'a'], 'key2' : ['one', 'one', 'two', 'three', 'two', 'two', 'one', 'three'], 'data1': np.random.randn(8), 'data2': np.random.randn(8)}>>> obj = pd.DataFrame(data)>>> obj key1 key2 data1 data20 a one -0.607009 1.9483011 b one 0.150818 -0.0250952 a two -2.086024 0.3581643 b three 0.446061 1.7087974 a two 0.745457 -0.9809485 b two 0.981877 2.1593276 a one 0.804480 -0.4996617 a three 0.112884 0.004367>>> >>> grouped = obj.groupby('key1')>>> list(grouped)[('a', key1 key2 data1 data20 a one -0.607009 1.9483012 a two -2.086024 0.3581644 a two 0.745457 -0.9809486 a one 0.804480 -0.4996617 a three 0.112884 0.004367),('b', key1 key2 data1 data21 b one 0.150818 -0.0250953 b three 0.446061 1.7087975 b two 0.981877 2.159327)]>>>>>> dict(list(grouped)){'a': key1 key2 data1 data20 a one -0.607009 1.9483012 a two -2.086024 0.3581644 a two 0.745457 -0.9809486 a one 0.804480 -0.4996617 a three 0.112884 0.004367,'b': key1 key2 data1 data21 b one 0.150818 -0.0250953 b three 0.446061 1.7087975 b two 0.981877 2.159327} 【04x00】GroupBy Apply 数据应用聚合指的是任何能够从数组产生标量值的数据转换过程,常用于对分组后的数据进行计算 【04x01】聚合函数之前的例子已经用过一些内置的聚合函数,比如 mean、count、min 以及 sum 等。常见的聚合运算如下表所示: 官方文档:https://pandas.pydata.org/docs/reference/groupby.html 方法 描述 count 非NA值的数量 describe 针对Series或各DataFrame列计算汇总统计 min 计算最小值 max 计算最大值 argmin 计算能够获取到最小值的索引位置(整数) argmax 计算能够获取到最大值的索引位置(整数) idxmin 计算能够获取到最小值的索引值 idxmax 计算能够获取到最大值的索引值 quantile 计算样本的分位数(0到1) sum 值的总和 mean 值的平均数 median 值的算术中位数(50%分位数) mad 根据平均值计算平均绝对离差 var 样本值的方差 std 样本值的标准差 应用示例: 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162>>> import pandas as pd>>> import numpy as np>>> obj = {'key1' : ['a', 'b', 'a', 'b', 'a', 'b', 'a', 'a'], 'key2' : ['one', 'one', 'two', 'three', 'two', 'two', 'one', 'three'], 'data1': np.random.randint(1,10, 8), 'data2': np.random.randint(1,10, 8)}>>> obj = pd.DataFrame(obj)>>> obj key1 key2 data1 data20 a one 9 71 b one 5 92 a two 2 43 b three 3 44 a two 5 15 b two 5 96 a one 1 87 a three 2 4>>> >>> obj.groupby('key1').sum() data1 data2key1 a 19 24b 13 22>>> >>> obj.groupby('key1').max() key2 data1 data2key1 a two 9 8b two 5 9>>> >>> obj.groupby('key1').min() key2 data1 data2key1 a one 1 1b one 3 4>>> >>> obj.groupby('key1').mean() data1 data2key1 a 3.800000 4.800000b 4.333333 7.333333>>> >>> obj.groupby('key1').size()key1a 5b 3dtype: int64>>> >>> obj.groupby('key1').count() key2 data1 data2key1 a 5 5 5b 3 3 3>>> >>> obj.groupby('key1').describe() data1 ... data2 count mean std min 25% ... min 25% 50% 75% maxkey1 ... a 5.0 3.800000 3.271085 1.0 2.0 ... 1.0 4.0 4.0 7.0 8.0b 3.0 4.333333 1.154701 3.0 4.0 ... 4.0 6.5 9.0 9.0 9.0[2 rows x 16 columns] 【04x02】自定义函数如果自带的内置函数满足不了我们的要求,则可以自定义一个聚合函数,然后传入 GroupBy.agg(func) 或 GroupBy.aggregate(func) 方法中即可。func 的参数为 groupby 索引对应的记录。 123456789101112131415161718192021222324252627282930313233>>> import pandas as pd>>> import numpy as np>>> obj = {'key1' : ['a', 'b', 'a', 'b', 'a', 'b', 'a', 'a'], 'key2' : ['one', 'one', 'two', 'three', 'two', 'two', 'one', 'three'], 'data1': np.random.randint(1,10, 8), 'data2': np.random.randint(1,10, 8)}>>> obj = pd.DataFrame(obj)>>> obj key1 key2 data1 data20 a one 9 71 b one 5 92 a two 2 43 b three 3 44 a two 5 15 b two 5 96 a one 1 87 a three 2 4>>> >>> def peak_range(df): return df.max() - df.min()>>> >>> obj.groupby('key1').agg(peak_range) data1 data2key1 a 8 7b 2 5>>> >>> obj.groupby('key1').agg(lambda df : df.max() - df.min()) data1 data2key1 a 8 7b 2 5 【04x03】对不同列作用不同函数使用字典可以对不同列作用不同的聚合函数: 123456789101112131415161718192021222324252627282930313233>>> import pandas as pd>>> import numpy as np>>> obj = {'key1' : ['a', 'b', 'a', 'b', 'a', 'b', 'a', 'a'], 'key2' : ['one', 'one', 'two', 'three', 'two', 'two', 'one', 'three'], 'data1': np.random.randint(1,10, 8), 'data2': np.random.randint(1,10, 8)}>>> obj = pd.DataFrame(obj)>>> obj key1 key2 data1 data20 a one 9 71 b one 5 92 a two 2 43 b three 3 44 a two 5 15 b two 5 96 a one 1 87 a three 2 4>>> >>> dict1 = {'data1':'mean', 'data2':'sum'}>>> dict2 = {'data1':['mean','max'], 'data2':'sum'}>>> >>> obj.groupby('key1').agg(dict1) data1 data2key1 a 3.800000 24b 4.333333 22>>> >>> obj.groupby('key1').agg(dict2) data1 data2 mean max sumkey1 a 3.800000 9 24b 4.333333 5 22 【04x04】GroupBy.apply()apply() 方法会将待处理的对象拆分成多个片段,然后对各片段调用传入的函数,最后尝试将各片段组合到一起。 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960>>> import pandas as pd>>> obj = pd.DataFrame({'A':['bob','sos','bob','sos','bob','sos','bob','bob'], 'B':['one','one','two','three','two','two','one','three'], 'C':[3,1,4,1,5,9,2,6], 'D':[1,2,3,4,5,6,7,8]})>>> obj A B C D0 bob one 3 11 sos one 1 22 bob two 4 33 sos three 1 44 bob two 5 55 sos two 9 66 bob one 2 77 bob three 6 8>>> >>> grouped = obj.groupby('A')>>> for name, group in grouped: print(name) print(group) bob A B C D0 bob one 3 12 bob two 4 34 bob two 5 56 bob one 2 77 bob three 6 8sos A B C D1 sos one 1 23 sos three 1 45 sos two 9 6>>> >>> grouped.apply(lambda x:x.describe()) # 对 bob 和 sos 两组数据使用 describe 方法 C DA bob count 5.000000 5.000000 mean 4.000000 4.800000 std 1.581139 2.863564 min 2.000000 1.000000 25% 3.000000 3.000000 50% 4.000000 5.000000 75% 5.000000 7.000000 max 6.000000 8.000000sos count 3.000000 3.000000 mean 3.666667 4.000000 std 4.618802 2.000000 min 1.000000 2.000000 25% 1.000000 3.000000 50% 1.000000 4.000000 75% 5.000000 5.000000 max 9.000000 6.000000>>>>>> grouped.apply(lambda x:x.min()) # # 对 bob 和 sos 两组数据使用 min 方法 A B C DA bob bob one 2 1sos sos one 1 2 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106804881未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Pandas","slug":"Python-数据分析/Pandas","permalink":"https://www.itrhx.com/categories/Python-数据分析/Pandas/"}],"tags":[{"name":"Pandas","slug":"Pandas","permalink":"https://www.itrhx.com/tags/Pandas/"},{"name":"GroupBy","slug":"GroupBy","permalink":"https://www.itrhx.com/tags/GroupBy/"},{"name":"数据分裂","slug":"数据分裂","permalink":"https://www.itrhx.com/tags/数据分裂/"},{"name":"数据合并","slug":"数据合并","permalink":"https://www.itrhx.com/tags/数据合并/"}]},{"title":"Python 数据分析三剑客之 Pandas(五):统计计算与统计描述","slug":"A83-Pandas-05","date":"2020-06-16T13:33:14.569Z","updated":"2020-07-06T13:44:54.663Z","comments":true,"path":"2020/06/16/A83-Pandas-05/","link":"","permalink":"https://www.itrhx.com/2020/06/16/A83-Pandas-05/","excerpt":"","text":"Pandas 系列文章: Python 数据分析三剑客之 Pandas(一):认识 Pandas 及其 Series、DataFrame 对象 Python 数据分析三剑客之 Pandas(二):Index 索引对象以及各种索引操作 Python 数据分析三剑客之 Pandas(三):算术运算与缺失值的处理 Python 数据分析三剑客之 Pandas(四):函数应用、映射、排序和层级索引 Python 数据分析三剑客之 Pandas(五):统计计算与统计描述 Python 数据分析三剑客之 Pandas(六):GroupBy 数据分裂、应用与合并 Python 数据分析三剑客之 Pandas(七):合并数据集 Python 数据分析三剑客之 Pandas(八):数据重塑、重复数据处理与数据替换 Python 数据分析三剑客之 Pandas(九):时间序列 Python 数据分析三剑客之 Pandas(十):数据读写 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106788501未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【01x00】统计计算Pandas 对象拥有一组常用的数学和统计方法。它们大部分都属于约简和汇总统计,用于从 Series 中提取单个值(如 sum 或 mean)或从 DataFrame 的行或列中提取一个 Series。跟对应的 NumPy 数组方法相比,它们都是基于没有缺失数据的假设而构建的。 【01x01】sum() 求和sum() 方法用于返回指定轴的和,相当于 numpy.sum()。 在 Series 和 DataFrame 中的基本语法如下: Series.sum(self, axis=None, skipna=None, level=None, numeric_only=None, min_count=0, **kwargs) DataFrame.sum(self, axis=None, skipna=None, level=None, numeric_only=None, min_count=0, **kwargs) 官方文档: https://pandas.pydata.org/docs/reference/api/pandas.Series.sum.html https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.sum.html 常用参数描述如下: 参数 描述 axis 指定轴求和,0 or ‘index’,1 or ‘columns’,只有在 DataFrame 中才有 1 or 'columns’ skipna bool 类型,求和时是否排除缺失值(NA/null),默认 True level 如果轴是 MultiIndex(层次结构),则沿指定层次求和 在 Series 中的应用: 12345678910111213141516171819202122232425262728>>> import pandas as pd>>> idx = pd.MultiIndex.from_arrays([ ['warm', 'warm', 'cold', 'cold'], ['dog', 'falcon', 'fish', 'spider']], names=['blooded', 'animal'])>>> obj = pd.Series([4, 2, 0, 8], name='legs', index=idx)>>> objblooded animalwarm dog 4 falcon 2cold fish 0 spider 8Name: legs, dtype: int64>>> >>> obj.sum()14>>> >>> obj.sum(level='blooded')bloodedwarm 6cold 8Name: legs, dtype: int64>>> >>> obj.sum(level=0)bloodedwarm 6cold 8Name: legs, dtype: int64 在 DataFrame 中的应用: 123456789101112131415161718192021222324>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame([[1.4, np.nan], [7.1, -4.5], [np.nan, np.nan], [0.75, -1.3]], index=['a', 'b', 'c', 'd'], columns=['one', 'two'])>>> obj one twoa 1.40 NaNb 7.10 -4.5c NaN NaNd 0.75 -1.3>>> >>> obj.sum()one 9.25two -5.80dtype: float64>>> >>> obj.sum(axis=1)a 1.40b 2.60c 0.00d -0.55dtype: float64 【01x02】min() 最小值min() 方法用于返回指定轴的最小值。 在 Series 和 DataFrame 中的基本语法如下: Series.min(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs) DataFrame.min(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs) 官方文档: https://pandas.pydata.org/docs/reference/api/pandas.Series.min.html https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.min.html 常用参数描述如下: 参数 描述 axis 指定轴求最小值,0 or ‘index’,1 or ‘columns’,只有在 DataFrame 中才有 1 or 'columns’ skipna bool 类型,求最小值时是否排除缺失值(NA/null),默认 True level 如果轴是 MultiIndex(层次结构),则沿指定层次求最小值 在 Series 中的应用: 12345678910111213141516171819202122232425262728>>> import pandas as pd>>> idx = pd.MultiIndex.from_arrays([ ['warm', 'warm', 'cold', 'cold'], ['dog', 'falcon', 'fish', 'spider']], names=['blooded', 'animal'])>>> obj = pd.Series([4, 2, 0, 8], name='legs', index=idx)>>> objblooded animalwarm dog 4 falcon 2cold fish 0 spider 8Name: legs, dtype: int64>>> >>> obj.min()0>>> >>> obj.min(level='blooded')bloodedwarm 2cold 0Name: legs, dtype: int64>>> >>> obj.min(level=0)bloodedwarm 2cold 0Name: legs, dtype: int64 在 DataFrame 中的应用: 123456789101112131415161718192021222324252627282930>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame([[1.4, np.nan], [7.1, -4.5], [np.nan, np.nan], [0.75, -1.3]], index=['a', 'b', 'c', 'd'],columns=['one', 'two'])>>> obj one twoa 1.40 NaNb 7.10 -4.5c NaN NaNd 0.75 -1.3>>> >>> obj.min()one 0.75two -4.50dtype: float64>>> >>> obj.min(axis=1)a 1.4b -4.5c NaNd -1.3dtype: float64>>> >>> obj.min(axis='columns', skipna=False)a NaNb -4.5c NaNd -1.3dtype: float64 【01x03】max() 最大值max() 方法用于返回指定轴的最大值。 在 Series 和 DataFrame 中的基本语法如下: Series.max(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs) DataFrame.max(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs) 官方文档: https://pandas.pydata.org/docs/reference/api/pandas.Series.max.html https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.max.html 常用参数描述如下: 参数 描述 axis 指定轴求最大值,0 or ‘index’,1 or ‘columns’,只有在 DataFrame 中才有 1 or 'columns’ skipna bool 类型,求最大值时是否排除缺失值(NA/null),默认 True level 如果轴是 MultiIndex(层次结构),则沿指定层次求最大值 在 Series 中的应用: 12345678910111213141516171819202122232425262728>>> import pandas as pd>>> idx = pd.MultiIndex.from_arrays([ ['warm', 'warm', 'cold', 'cold'], ['dog', 'falcon', 'fish', 'spider']], names=['blooded', 'animal'])>>> obj = pd.Series([4, 2, 0, 8], name='legs', index=idx)>>> objblooded animalwarm dog 4 falcon 2cold fish 0 spider 8Name: legs, dtype: int64>>> >>> obj.max()8>>> >>> obj.max(level='blooded')bloodedwarm 4cold 8Name: legs, dtype: int64>>> >>> obj.max(level=0)bloodedwarm 4cold 8Name: legs, dtype: int64 在 DataFrame 中的应用: 123456789101112131415161718192021222324252627282930>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame([[1.4, np.nan], [7.1, -4.5], [np.nan, np.nan], [0.75, -1.3]], index=['a', 'b', 'c', 'd'],columns=['one', 'two'])>>> obj one twoa 1.40 NaNb 7.10 -4.5c NaN NaNd 0.75 -1.3>>> >>> obj.max()one 7.1two -1.3dtype: float64>>> >>> obj.max(axis=1)a 1.40b 7.10c NaNd 0.75dtype: float64>>> >>> obj.max(axis='columns', skipna=False)a NaNb 7.10c NaNd 0.75dtype: float64 【01x04】mean() 平均值mean() 方法用于返回指定轴的平均值。 在 Series 和 DataFrame 中的基本语法如下: Series.mean(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs) DataFrame.mean(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs) 官方文档: https://pandas.pydata.org/docs/reference/api/pandas.Series.mean.html https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.mean.html 常用参数描述如下: 参数 描述 axis 指定轴求平均值,0 or ‘index’,1 or ‘columns’,只有在 DataFrame 中才有 1 or 'columns’ skipna bool 类型,求平均值时是否排除缺失值(NA/null),默认 True level 如果轴是 MultiIndex(层次结构),则沿指定层次求平均值 在 Series 中的应用: 12345678910111213141516171819202122232425262728>>> import pandas as pd>>> idx = pd.MultiIndex.from_arrays([ ['warm', 'warm', 'cold', 'cold'], ['dog', 'falcon', 'fish', 'spider']], names=['blooded', 'animal'])>>> obj = pd.Series([4, 2, 0, 8], name='legs', index=idx)>>> objblooded animalwarm dog 4 falcon 2cold fish 0 spider 8Name: legs, dtype: int64>>> >>> obj.mean()3.5>>> >>> obj.mean(level='blooded')bloodedwarm 3cold 4Name: legs, dtype: int64>>> >>> obj.mean(level=0)bloodedwarm 3cold 4Name: legs, dtype: int64 在 DataFrame 中的应用: 123456789101112131415161718192021222324252627282930>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame([[1.4, np.nan], [7.1, -4.5], [np.nan, np.nan], [0.75, -1.3]], index=['a', 'b', 'c', 'd'],columns=['one', 'two'])>>> obj one twoa 1.40 NaNb 7.10 -4.5c NaN NaNd 0.75 -1.3>>> >>> obj.mean()one 3.083333two -2.900000dtype: float64>>> >>> obj.mean(axis=1)a 1.400b 1.300c NaNd -0.275dtype: float64>>> >>> obj.mean(axis='columns', skipna=False)a NaNb 1.300c NaNd -0.275dtype: float64 【01x05】idxmin() 最小值索引idxmin() 方法用于返回最小值的索引。 在 Series 和 DataFrame 中的基本语法如下: Series.idxmin(self, axis=0, skipna=True, *args, **kwargs) DataFrame.idxmin(self, axis=0, skipna=True) 官方文档: https://pandas.pydata.org/docs/reference/api/pandas.Series.idxmin.html https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.idxmin.html 常用参数描述如下: 参数 描述 axis 指定轴,0 or ‘index’,1 or ‘columns’,只有在 DataFrame 中才有 1 or 'columns’ skipna bool 类型,是否排除缺失值(NA/null),默认 True 在 Series 中的应用: 12345678910111213141516>>> import pandas as pd>>> idx = pd.MultiIndex.from_arrays([ ['warm', 'warm', 'cold', 'cold'], ['dog', 'falcon', 'fish', 'spider']], names=['blooded', 'animal'])>>> obj = pd.Series([4, 2, 0, 8], name='legs', index=idx)>>> objblooded animalwarm dog 4 falcon 2cold fish 0 spider 8Name: legs, dtype: int64>>> >>> obj.idxmin()('cold', 'fish') 在 DataFrame 中的应用: 12345678910111213141516>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame([[1.4, np.nan], [7.1, -4.5], [np.nan, np.nan], [0.75, -1.3]], index=['a', 'b', 'c', 'd'],columns=['one', 'two'])>>> obj one twoa 1.40 NaNb 7.10 -4.5c NaN NaNd 0.75 -1.3>>> >>> obj.idxmin()one dtwo bdtype: object 【01x06】idxmax() 最大值索引idxmax() 方法用于返回最大值的索引。 在 Series 和 DataFrame 中的基本语法如下: Series.idxmax(self, axis=0, skipna=True, *args, **kwargs) DataFrame.idxmax(self, axis=0, skipna=True) 官方文档: https://pandas.pydata.org/docs/reference/api/pandas.Series.idxmax.html https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.idxmax.html 常用参数描述如下: 参数 描述 axis 指定轴,0 or ‘index’,1 or ‘columns’,只有在 DataFrame 中才有 1 or 'columns’ skipna bool 类型,是否排除缺失值(NA/null),默认 True 在 Series 中的应用: 12345678910111213141516>>> import pandas as pd>>> idx = pd.MultiIndex.from_arrays([ ['warm', 'warm', 'cold', 'cold'], ['dog', 'falcon', 'fish', 'spider']], names=['blooded', 'animal'])>>> obj = pd.Series([4, 2, 0, 8], name='legs', index=idx)>>> objblooded animalwarm dog 4 falcon 2cold fish 0 spider 8Name: legs, dtype: int64>>> >>> obj.idxmax()('cold', 'spider') 在 DataFrame 中的应用: 12345678910111213141516>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame([[1.4, np.nan], [7.1, -4.5], [np.nan, np.nan], [0.75, -1.3]], index=['a', 'b', 'c', 'd'],columns=['one', 'two'])>>> obj one twoa 1.40 NaNb 7.10 -4.5c NaN NaNd 0.75 -1.3>>> >>> obj.idxmax()one btwo ddtype: object 【02x00】统计描述describe() 方法用于快速综合统计结果:计数、均值、标准差、最大最小值、四分位数等。还可以通过参数来设置需要忽略或者包含的统计选项。 在 Series 和 DataFrame 中的基本语法如下: Series.describe(self: ~ FrameOrSeries, percentiles=None, include=None, exclude=None) DataFrame.describe(self: ~ FrameOrSeries, percentiles=None, include=None, exclude=None) 官方文档: https://pandas.pydata.org/docs/reference/api/pandas.Series.describe.html https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.describe.html 参数 描述 percentiles 数字列表,可选项,要包含在输出中的百分比。所有值都应介于 0 和 1 之间。默认值为 [.25、.5、.75],即返回第 25、50 和 75 个百分点 include 要包含在结果中的数据类型,数据类型列表,默认 None,具体取值类型参见官方文档 exclude 要从结果中忽略的数据类型,数据类型列表,默认 None,具体取值类型参见官方文档 描述数字形式的 Series 对象: 123456789101112131415161718>>> import pandas as pd>>> obj = pd.Series([1, 2, 3])>>> obj0 11 22 3dtype: int64>>> >>> obj.describe()count 3.0mean 2.0std 1.0min 1.025% 1.550% 2.075% 2.5max 3.0dtype: float64 分类描述: 123456789101112131415>>> import pandas as pd>>> obj = pd.Series(['a', 'a', 'b', 'c'])>>> obj0 a1 a2 b3 cdtype: object>>> >>> obj.describe()count 4unique 3top afreq 2dtype: object 描述时间戳: 1234567891011121314151617181920>>> import pandas as pd>>> obj = pd.Series([ np.datetime64(\"2000-01-01\"), np.datetime64(\"2010-01-01\"), np.datetime64(\"2010-01-01\") ])>>> obj0 2000-01-011 2010-01-012 2010-01-01dtype: datetime64[ns]>>> >>> obj.describe()count 3unique 2top 2010-01-01 00:00:00freq 2first 2000-01-01 00:00:00last 2010-01-01 00:00:00dtype: object 描述 DataFrame 对象: 123456789101112131415161718>>> import pandas as pd>>> obj = pd.DataFrame({'categorical': pd.Categorical(['d','e','f']), 'numeric': [1, 2, 3], 'object': ['a', 'b', 'c']})>>> obj categorical numeric object0 d 1 a1 e 2 b2 f 3 c>>> >>> obj.describe() numericcount 3.0mean 2.0std 1.0min 1.025% 1.550% 2.075% 2.5max 3.0 不考虑数据类型,显示所有描述: 123456789101112131415161718192021>>> import pandas as pd>>> obj = pd.DataFrame({'categorical': pd.Categorical(['d','e','f']), 'numeric': [1, 2, 3], 'object': ['a', 'b', 'c']})>>> obj categorical numeric object0 d 1 a1 e 2 b2 f 3 c>>> >>> obj.describe(include='all') categorical numeric objectcount 3 3.0 3unique 3 NaN 3top f NaN cfreq 1 NaN 1mean NaN 2.0 NaNstd NaN 1.0 NaNmin NaN 1.0 NaN25% NaN 1.5 NaN50% NaN 2.0 NaN75% NaN 2.5 NaNmax NaN 3.0 NaN 仅包含 category 列: 1234567891011121314>>> import pandas as pd>>> obj = pd.DataFrame({'categorical': pd.Categorical(['d','e','f']), 'numeric': [1, 2, 3], 'object': ['a', 'b', 'c']})>>> obj categorical numeric object0 d 1 a1 e 2 b2 f 3 c>>> >>> obj.describe(include=['category']) categoricalcount 3unique 3top ffreq 1 【03x00】常用统计方法其他常用统计方法参见下表: 方法 描述 官方文档 count 非NA值的数量 Series丨DataFrame describe 针对Series或各DataFrame列计算汇总统计 Series丨DataFrame min 计算最小值 Series丨DataFrame max 计算最大值 Series丨DataFrame argmin 计算能够获取到最小值的索引位置(整数) Series argmax 计算能够获取到最大值的索引位置(整数) Series idxmin 计算能够获取到最小值的索引值 Series丨DataFrame idxmax 计算能够获取到最大值的索引值 Series丨DataFrame quantile 计算样本的分位数(0到1) Series丨DataFrame sum 值的总和 Series丨DataFrame mean 值的平均数 Series丨DataFrame median 值的算术中位数(50%分位数) Series丨DataFrame mad 根据平均值计算平均绝对离差 Series丨DataFrame var 样本值的方差 Series丨DataFrame std 样本值的标准差 Series丨DataFrame 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106788501未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Pandas","slug":"Python-数据分析/Pandas","permalink":"https://www.itrhx.com/categories/Python-数据分析/Pandas/"}],"tags":[{"name":"Pandas","slug":"Pandas","permalink":"https://www.itrhx.com/tags/Pandas/"},{"name":"统计计算","slug":"统计计算","permalink":"https://www.itrhx.com/tags/统计计算/"},{"name":"统计描述","slug":"统计描述","permalink":"https://www.itrhx.com/tags/统计描述/"}]},{"title":"Python 数据分析三剑客之 Pandas(四):函数应用/映射/排序和层级索引","slug":"A82-Pandas-04","date":"2020-06-15T12:42:36.626Z","updated":"2020-07-06T13:44:46.745Z","comments":true,"path":"2020/06/15/A82-Pandas-04/","link":"","permalink":"https://www.itrhx.com/2020/06/15/A82-Pandas-04/","excerpt":"","text":"Pandas 系列文章: Python 数据分析三剑客之 Pandas(一):认识 Pandas 及其 Series、DataFrame 对象 Python 数据分析三剑客之 Pandas(二):Index 索引对象以及各种索引操作 Python 数据分析三剑客之 Pandas(三):算术运算与缺失值的处理 Python 数据分析三剑客之 Pandas(四):函数应用、映射、排序和层级索引 Python 数据分析三剑客之 Pandas(五):统计计算与统计描述 Python 数据分析三剑客之 Pandas(六):GroupBy 数据分裂、应用与合并 Python 数据分析三剑客之 Pandas(七):合并数据集 Python 数据分析三剑客之 Pandas(八):数据重塑、重复数据处理与数据替换 Python 数据分析三剑客之 Pandas(九):时间序列 Python 数据分析三剑客之 Pandas(十):数据读写 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106758103未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【01x00】函数应用和映射Pandas 可直接使用 NumPy 的 ufunc(元素级数组方法) 函数: 123456789101112131415161718>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame(np.random.randn(5,4) - 1)>>> obj 0 1 2 30 -0.228107 1.377709 -1.096528 -2.0510011 -2.477144 -0.500013 -0.040695 -0.2674522 -0.485999 -1.232930 -0.390701 -1.9479843 -0.839161 -0.702802 -1.756359 -1.8731494 0.853121 -1.540105 0.621614 -0.583360>>> >>> np.abs(obj) 0 1 2 30 0.228107 1.377709 1.096528 2.0510011 2.477144 0.500013 0.040695 0.2674522 0.485999 1.232930 0.390701 1.9479843 0.839161 0.702802 1.756359 1.8731494 0.853121 1.540105 0.621614 0.583360 函数映射:在 Pandas 中 apply 方法可以将函数应用到列或行上,可以通过设置 axis 参数来指定行或列,默认 axis = 0,即按列映射: 12345678910111213141516171819202122232425>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame(np.random.randn(5,4) - 1)>>> obj 0 1 2 30 -0.707028 -0.755552 -2.196480 -0.5296761 -0.772668 0.127485 -2.015699 -0.2836542 0.248200 -1.940189 -1.068028 -1.7517373 -0.872904 -0.465371 -1.327951 -2.8831604 -0.092664 0.258351 -1.010747 -2.313039>>> >>> obj.apply(lambda x : x.max())0 0.2482001 0.2583512 -1.0107473 -0.283654dtype: float64>>>>>> obj.apply(lambda x : x.max(), axis=1)0 -0.5296761 0.1274852 0.2482003 -0.4653714 0.258351dtype: float64 另外还可以通过 applymap 将函数映射到每个数据上: 123456789101112131415161718>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame(np.random.randn(5,4) - 1)>>> obj 0 1 2 30 -0.772463 -1.597008 -3.196100 -1.9484861 -1.765108 -1.646421 -0.687175 -0.4017822 0.275699 -3.115184 -1.429063 -1.0756103 -0.251734 -0.448399 -3.077677 -0.2946744 -1.495896 -1.689729 -0.560376 -1.808794>>> >>> obj.applymap(lambda x : '%.2f' % x) 0 1 2 30 -0.77 -1.60 -3.20 -1.951 -1.77 -1.65 -0.69 -0.402 0.28 -3.12 -1.43 -1.083 -0.25 -0.45 -3.08 -0.294 -1.50 -1.69 -0.56 -1.81 【02x00】排序【02x01】sort_index() 索引排序根据条件对数据集排序(sorting)也是一种重要的内置运算。要对行或列索引进行排序(按字典顺序),可使用 sort_index 方法,它将返回一个已排序的新对象。 在 Series 和 DataFrame 中的基本语法如下: 123456789Series.sort_index(self, axis=0, level=None, ascending=True, inplace=False, kind='quicksort', na_position='last', sort_remaining=True, ignore_index: bool = False) 123456789DataFrame.sort_index(self, axis=0, level=None, ascending=True, inplace=False, kind='quicksort', na_position='last', sort_remaining=True, ignore_index: bool = False) 官方文档: https://pandas.pydata.org/docs/reference/api/pandas.Series.sort_index.html https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.sort_index.html 常用参数描述如下: 参数 描述 axis 指定轴排序,0 or ‘index’,1 or ‘columns’,只有在 DataFrame 中才有 1 or 'columns’ ascending 为 True时升序排序(默认),为 False时降序排序 kind 排序方法,quicksort:快速排序(默认);'mergesort’:归并排序;'heapsort':堆排序;具体可参见 numpy.sort() 在 Series 中的应用(按照索引 index 排序): 123456789101112131415>>> import pandas as pd>>> obj = pd.Series(range(4), index=['d', 'a', 'b', 'c'])>>> objd 0a 1b 2c 3dtype: int64>>> >>> obj.sort_index()a 1b 2c 3d 0dtype: int64 在 DataFrame 中的应用(可按照索引 index 或列标签 columns 排序): 123456789101112131415161718192021>>> import pandas as pd>>> obj = pd.DataFrame(np.arange(8).reshape((2, 4)), index=['three', 'one'], columns=['d', 'a', 'b', 'c'])>>> obj d a b cthree 0 1 2 3one 4 5 6 7>>> >>> obj.sort_index() d a b cone 4 5 6 7three 0 1 2 3>>> >>> obj.sort_index(axis=1) a b c dthree 1 2 3 0one 5 6 7 4>>> >>> obj.sort_index(axis=1, ascending=False) d c b athree 0 3 2 1one 4 7 6 5 【02x02】sort_values() 按值排序在 Series 和 DataFrame 中的基本语法如下: 1234567Series.sort_values(self, axis=0, ascending=True, inplace=False, kind='quicksort', na_position='last', ignore_index=False) 12345678DataFrame.sort_values(self, by, axis=0, ascending=True, inplace=False, kind='quicksort', na_position='last', ignore_index=False) 官方文档: https://pandas.pydata.org/docs/reference/api/pandas.Series.sort_values.html https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.sort_values.html 常用参数描述如下: 参数 描述 by DataFrame 中的必须参数,指定列的值进行排序,Series 中没有此参数 axis 指定轴排序,0 or ‘index’,1 or ‘columns’,只有在 DataFrame 中才有 1 or 'columns’ ascending 为 True时升序排序(默认),为 False时降序排序 kind 排序方法,quicksort:快速排序(默认);'mergesort’:归并排序;'heapsort':堆排序;具体可参见 numpy.sort() 在 Series 中的应用,按照值排序,如果有缺失值,默认都会被放到 Series 的末尾: 12345678910111213141516171819202122232425262728293031323334>>> import pandas as pd>>> obj = pd.Series([4, 7, -3, 2])>>> obj0 41 72 -33 2dtype: int64>>> >>> obj.sort_values()2 -33 20 41 7dtype: int64>>> >>> obj = pd.Series([4, np.nan, 7, np.nan, -3, 2])>>> obj0 4.01 NaN2 7.03 NaN4 -3.05 2.0dtype: float64>>> >>> obj.sort_values()4 -3.05 2.00 4.02 7.01 NaN3 NaNdtype: float64 在 DataFrame 中的应用,有时候可能希望根据一个或多个列中的值进行排序。将一个或多个列的名字传递给 sort_values() 的 by 参数即可达到该目的,当传递多个列时,首先会对第一列进行排序,若第一列有相同的值,再根据第二列进行排序,依次类推: 1234567891011121314151617181920212223242526272829>>> import pandas as pd>>> obj = pd.DataFrame({'a': [4, 4, -3, 2], 'b': [0, 1, 0, 1], 'c': [6, 4, 1, 3]})>>> obj a b c0 4 0 61 4 1 42 -3 0 13 2 1 3>>> >>> obj.sort_values(by='c') a b c2 -3 0 13 2 1 31 4 1 40 4 0 6>>> >>> obj.sort_values(by='c', ascending=False) a b c0 4 0 61 4 1 43 2 1 32 -3 0 1>>>>>> obj.sort_values(by=['a', 'b']) a b c2 -3 0 13 2 1 30 4 0 61 4 1 4 123456789101112131415>>> import pandas as pd>>> obj = pd.DataFrame({'a': [4, 4, -3, 2], 'b': [0, 1, 0, 1], 'c': [6, 4, 1, 3]}, index=['A', 'B', 'C', 'D'])>>> obj a b cA 4 0 6B 4 1 4C -3 0 1D 2 1 3>>> >>> obj.sort_values(by='B', axis=1) b a cA 0 4 6B 1 4 4C 0 -3 1D 1 2 3 【02x03】rank() 返回排序后元素索引rank() 函数会返回一个对象,对象的值是原对象经过排序后的索引值,即下标。 在 Series 和 DataFrame 中的基本语法如下: 1234567Series.rank(self: ~ FrameOrSeries, axis=0, method: str = 'average', numeric_only: Union[bool, NoneType] = None, na_option: str = 'keep', ascending: bool = True, pct: bool = False) 1234567DataFrame.rank(self: ~ FrameOrSeries, axis=0, method: str = 'average', numeric_only: Union[bool, NoneType] = None, na_option: str = 'keep', ascending: bool = True, pct: bool = False) 官方文档: https://pandas.pydata.org/docs/reference/api/pandas.Series.rank.html https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.rank.html 常用参数描述如下: 参数 描述 axis 指定轴排序,0 or ‘index’,1 or ‘columns’,只有在 DataFrame 中才有 1 or 'columns’ method 有相同值时,如何处理:‘average’:默认值,去两个相同索引的平均值;‘min’:取两个相同索引的最小值;‘max’:取两个相同索引的最大值;‘first’:按照出现的先后顺序;‘dense’:和 'min' 差不多,但是各组之间总是+1的,不太好解释,可以看后面的示例 ascending 为 True时升序排序(默认),为 False时降序排序 在 Series 中的应用,按照值排序,如果有缺失值,默认都会被放到 Series 的末尾: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051>>> import pandas as pd>>> obj = pd.Series([7, -5, 7, 4, 2, 0, 4])>>> obj0 71 -52 73 44 25 06 4dtype: int64>>> >>> obj.rank()0 6.5 # 第 0 个和第 2 个值从小到大排名分别为 6 和 7,默认取平均值,即 6.51 1.02 6.53 4.5 # 第 3 个和第 6 个值从小到大排名分别为 4 和 5,默认取平均值,即 4.54 3.05 2.06 4.5dtype: float64>>> >>> obj.rank(method='first')0 6.0 # 第 0 个和第 2 个值从小到大排名分别为 6 和 7,按照第一次出现排序,分别为 6 和 71 1.02 7.03 4.0 # 第 3 个和第 6 个值从小到大排名分别为 4 和 5,按照第一次出现排序,分别为 4 和 54 3.05 2.06 5.0dtype: float64>>> >>> obj.rank(method='dense')0 5.0 # 第 0 个和第 2 个值从小到大排名分别为 6 和 7,按照最小值排序,但 dense 规定间隔为 1 所以为 51 1.02 5.03 4.0 # 第 3 个和第 6 个值从小到大排名分别为 4 和 5,按照最小值排序,即 44 3.05 2.06 4.0dtype: float64>>> >>> obj.rank(method='min')0 6.0 # 第 0 个和第 2 个值从小到大排名分别为 6 和 7,按照最小值排序,即 61 1.02 6.03 4.0 # 第 3 个和第 6 个值从小到大排名分别为 4 和 5,按照最小值排序,即 44 3.05 2.06 4.0dtype: float64 在 DataFrame 中可以使用 axis 参数来指定轴: 12345678910111213141516171819202122>>> import pandas as pd>>> obj = pd.DataFrame({'b': [4.3, 7, -3, 2], 'a': [0, 1, 0, 1], 'c': [-2, 5, 8, -2.5]})>>> obj b a c0 4.3 0 -2.01 7.0 1 5.02 -3.0 0 8.03 2.0 1 -2.5>>> >>> obj.rank() b a c0 3.0 1.5 2.01 4.0 3.5 3.02 1.0 1.5 4.03 2.0 3.5 1.0>>> >>> obj.rank(axis='columns') b a c0 3.0 2.0 1.01 3.0 1.0 2.02 1.0 2.0 3.03 3.0 2.0 1.0 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106758103未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【03x00】层级索引【03x01】认识层级索引以下示例将创建一个 Series 对象, 索引 Index 由两个子 list 组成,第一个子 list 是外层索引,第二个 list 是内层索引: 1234567891011121314151617>>> import pandas as pd>>> import numpy as np>>> obj = pd.Series(np.random.randn(12),index=[['a', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'c', 'd', 'd', 'd'], [0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2]])>>> obja 0 -0.201536 1 -0.629058 2 0.766716b 0 -1.255831 1 -0.483727 2 -0.018653c 0 0.788787 1 1.010097 2 -0.187258d 0 1.242363 1 -0.822011 2 -0.085682dtype: float64 【03x02】MultiIndex 索引对象官方文档:https://pandas.pydata.org/docs/reference/api/pandas.MultiIndex.html 尝试打印上面示例中 Series 的索引类型,会得到一个 MultiIndex 对象,MultiIndex 对象的 levels 属性表示两个层级中分别有那些标签,codes 属性表示每个位置分别是什么标签,如下所示: 12345678910111213141516171819202122232425262728293031323334353637383940>>> import pandas as pd>>> import numpy as np>>> obj = pd.Series(np.random.randn(12),index=[['a', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'c', 'd', 'd', 'd'], [0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2]])>>> obja 0 0.035946 1 -0.867215 2 -0.053355b 0 -0.986616 1 0.026071 2 -0.048394c 0 0.251274 1 0.217790 2 1.137674d 0 -1.245178 1 1.234972 2 -0.035624dtype: float64>>> >>> type(obj.index)<class 'pandas.core.indexes.multi.MultiIndex'>>>> >>> obj.indexMultiIndex([('a', 0), ('a', 1), ('a', 2), ('b', 0), ('b', 1), ('b', 2), ('c', 0), ('c', 1), ('c', 2), ('d', 0), ('d', 1), ('d', 2)], )>>> obj.index.levelsFrozenList([['a', 'b', 'c', 'd'], [0, 1, 2]])>>>>>> obj.index.codesFrozenList([[0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3], [0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2]]) 通常可以使用 from_arrays() 方法来将数组对象转换为 MultiIndex 索引对象: 1234567>>> arrays = [[1, 1, 2, 2], ['red', 'blue', 'red', 'blue']]>>> pd.MultiIndex.from_arrays(arrays, names=('number', 'color'))MultiIndex([(1, 'red'), (1, 'blue'), (2, 'red'), (2, 'blue')], names=['number', 'color']) 其他常用方法见下表(更多方法参见官方文档): 方法 描述 from_arrays(arrays[, sortorder, names]) 将数组转换为 MultiIndex from_tuples(tuples[, sortorder, names]) 将元组列表转换为 MultiIndex from_product(iterables[, sortorder, names]) 将多个可迭代的笛卡尔积转换成 MultiIndex from_frame(df[, sortorder, names]) 将 DataFrame 对象转换为 MultiIndex set_levels(self, levels[, level, inplace, …]) 为 MultiIndex 设置新的 levels set_codes(self, codes[, level, inplace, …]) 为 MultiIndex 设置新的 codes sortlevel(self[, level, ascending, …]) 根据 level 进行排序 droplevel(self[, level]) 删除指定的 level swaplevel(self[, i, j]) 交换 level i 与 level i,即交换外层索引与内层索引 【03x03】提取值对于这种有多层索引的对象,如果只传入一个参数,则会对外层索引进行提取,其中包含对应所有的内层索引,如果传入两个参数,则第一个参数表示外层索引,第二个参数表示内层索引,示例如下: 123456789101112131415161718192021222324252627282930313233>>> import pandas as pd>>> import numpy as np>>> obj = pd.Series(np.random.randn(12),index=[['a', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'c', 'd', 'd', 'd'], [0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2]])>>> obja 0 0.550202 1 0.328784 2 1.422690b 0 -1.333477 1 -0.933809 2 -0.326541c 0 0.663686 1 0.943393 2 0.273106d 0 1.354037 1 -2.312847 2 -2.343777dtype: float64>>> >>> obj['b']0 -1.3334771 -0.9338092 -0.326541dtype: float64>>>>>> obj['b', 1]-0.9338094811708413>>> >>> obj[:, 2]a 1.422690b -0.326541c 0.273106d -2.343777dtype: float64 【03x04】交换分层与排序MultiIndex 对象的 swaplevel() 方法可以交换外层与内层索引,sortlevel() 方法会先对外层索引进行排序,再对内层索引进行排序,默认是升序,如果设置 ascending 参数为 False 则会降序排列,示例如下: 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647>>> import pandas as pd>>> import numpy as np>>> obj = pd.Series(np.random.randn(12),index=[['a', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'c', 'd', 'd', 'd'], [0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2]])>>> obja 0 -0.110215 1 0.193075 2 -1.101706b 0 -1.325743 1 0.528418 2 -0.127081c 0 -0.733822 1 1.665262 2 0.127073d 0 1.262022 1 -1.170518 2 0.966334dtype: float64>>> >>> obj.swaplevel()0 a -0.1102151 a 0.1930752 a -1.1017060 b -1.3257431 b 0.5284182 b -0.1270810 c -0.7338221 c 1.6652622 c 0.1270730 d 1.2620221 d -1.1705182 d 0.966334dtype: float64>>> >>> obj.swaplevel().index.sortlevel()(MultiIndex([(0, 'a'), (0, 'b'), (0, 'c'), (0, 'd'), (1, 'a'), (1, 'b'), (1, 'c'), (1, 'd'), (2, 'a'), (2, 'b'), (2, 'c'), (2, 'd')], ), array([ 0, 3, 6, 9, 1, 4, 7, 10, 2, 5, 8, 11], dtype=int32)) 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106758103未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Pandas","slug":"Python-数据分析/Pandas","permalink":"https://www.itrhx.com/categories/Python-数据分析/Pandas/"}],"tags":[{"name":"Pandas","slug":"Pandas","permalink":"https://www.itrhx.com/tags/Pandas/"},{"name":"函数应用","slug":"函数应用","permalink":"https://www.itrhx.com/tags/函数应用/"},{"name":"映射","slug":"映射","permalink":"https://www.itrhx.com/tags/映射/"},{"name":"排序","slug":"排序","permalink":"https://www.itrhx.com/tags/排序/"},{"name":"层级索引","slug":"层级索引","permalink":"https://www.itrhx.com/tags/层级索引/"}]},{"title":"Python 数据分析三剑客之 Pandas(三):算术运算与缺失值的处理","slug":"A81-Pandas-03","date":"2020-06-14T15:36:58.366Z","updated":"2020-07-06T13:44:36.650Z","comments":true,"path":"2020/06/14/A81-Pandas-03/","link":"","permalink":"https://www.itrhx.com/2020/06/14/A81-Pandas-03/","excerpt":"","text":"Pandas 系列文章: Python 数据分析三剑客之 Pandas(一):认识 Pandas 及其 Series、DataFrame 对象 Python 数据分析三剑客之 Pandas(二):Index 索引对象以及各种索引操作 Python 数据分析三剑客之 Pandas(三):算术运算与缺失值的处理 Python 数据分析三剑客之 Pandas(四):函数应用、映射、排序和层级索引 Python 数据分析三剑客之 Pandas(五):统计计算与统计描述 Python 数据分析三剑客之 Pandas(六):GroupBy 数据分裂、应用与合并 Python 数据分析三剑客之 Pandas(七):合并数据集 Python 数据分析三剑客之 Pandas(八):数据重塑、重复数据处理与数据替换 Python 数据分析三剑客之 Pandas(九):时间序列 Python 数据分析三剑客之 Pandas(十):数据读写 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106743778未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【01x00】Pandas 算术运算Pandas 继承了 NumPy 的功能,NumPy 的基本能力之一是快速对每个元素进行运算,既包括基本算术运算(加、减、乘、除),也包括更复杂的运算(三角函数、指数函数和对数函数等)。具体可以参考 NumPy 系列文章。 【01x01】使用 NumPy 通用函数因为 Pandas 是建立在 NumPy 基础之上的,所以 NumPy 的通用函数同样适用于 Pandas 的 Series 和 DataFrame 对象,如下所示: 1234567891011121314151617>>> import pandas as pd>>> import numpy as np>>> rng = np.random.RandomState(42)>>> ser = pd.Series(rng.randint(0, 10, 4))>>> ser0 61 32 73 4dtype: int32>>> >>> obj = pd.DataFrame(rng.randint(0, 10, (3, 4)), columns=['A', 'B', 'C', 'D'])>>> obj A B C D0 6 9 2 61 7 4 3 72 7 2 5 4 使用 NumPy 通用函数,生成的结果是另一个保留索引的 Pandas 对象: 1234567891011121314151617>>> import pandas as pd>>> import numpy as np>>> rng = np.random.RandomState(42)>>> ser = pd.Series(rng.randint(0, 10, 4))>>> ser0 61 32 73 4dtype: int32>>> >>> np.exp(ser)0 403.4287931 20.0855372 1096.6331583 54.598150dtype: float64 12345678>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame(rng.randint(0, 10, (3, 4)), columns=['A', 'B', 'C', 'D'])>>> np.sin(obj * np.pi / 4) A B C D0 -1.000000 7.071068e-01 1.000000 -1.000000e+001 -0.707107 1.224647e-16 0.707107 -7.071068e-012 -0.707107 1.000000e+00 -0.707107 1.224647e-16 【01x02】数据对齐Pandas 最重要的一个功能是,它可以对不同索引的对象进行算术运算。在将对象相加时,如果存在不同的索引对,则结果的索引就是该索引对的并集。自动的数据对齐操作会在不重叠的索引处引入缺失值,即 NaN,缺失值会在算术运算过程中传播。 Series 对象的数据对齐操作: 1234567891011121314151617181920212223242526>>> import pandas as pd>>> obj1 = pd.Series([7.3, -2.5, 3.4, 1.5], index=['a', 'c', 'd', 'e'])>>> obj2 = pd.Series([-2.1, 3.6, -1.5, 4, 3.1], index=['a', 'c', 'e', 'f', 'g'])>>> obj1a 7.3c -2.5d 3.4e 1.5dtype: float64>>> >>> obj2a -2.1c 3.6e -1.5f 4.0g 3.1dtype: float64>>>>>> obj1 + obj2a 5.2c 1.1d NaNe 0.0f NaNg NaNdtype: float64 DataFrame 对象的数据对齐操作会同时发生在行和列上: 1234567891011121314151617181920212223>>> import pandas as pd>>> obj1 = pd.DataFrame(np.arange(9.).reshape((3, 3)), columns=list('bcd'), index=['Ohio', 'Texas', 'Colorado'])>>> obj2 = pd.DataFrame(np.arange(12.).reshape((4, 3)), columns=list('bde'), index=['Utah', 'Ohio', 'Texas', 'Oregon'])>>> obj1 b c dOhio 0.0 1.0 2.0Texas 3.0 4.0 5.0Colorado 6.0 7.0 8.0>>> >>> obj2 b d eUtah 0.0 1.0 2.0Ohio 3.0 4.0 5.0Texas 6.0 7.0 8.0Oregon 9.0 10.0 11.0>>> >>> obj1 + obj2 b c d eColorado NaN NaN NaN NaNOhio 3.0 NaN 6.0 NaNOregon NaN NaN NaN NaNTexas 9.0 NaN 12.0 NaNUtah NaN NaN NaN NaN 【01x03】DataFrame 与 Series 之间的运算首先回忆 NumPy 中的广播(参见:《Python 数据分析三剑客之 NumPy(二):数组索引 / 切片 / 广播 / 拼接 / 分割》),跟不同维度的 NumPy 数组一样,DataFrame 和 Series 之间算术运算也是有明确规定的。首先回忆一下 NumPy 中不同维度的数组之间的运算: 1234567891011121314>>> import numpy as np>>> arr = np.arange(12.).reshape((3, 4))>>> arrarray([[ 0., 1., 2., 3.], [ 4., 5., 6., 7.], [ 8., 9., 10., 11.]])>>> >>> arr[0]array([0., 1., 2., 3.])>>> >>> arr - arr[0]array([[0., 0., 0., 0.], [4., 4., 4., 4.], [8., 8., 8., 8.]]) 可以看到每一行都进行了减法运算,这正是 NumPy 中的广播,而 DataFrame 与 Series 之间的运算也类似,默认情况下,DataFrame 和 Series 之间的算术运算会将 Series 的索引匹配到 DataFrame 的列,然后沿着行一直向下广播: 1234567891011121314151617181920212223>>> import numpy as np>>> import pandas as pd>>> frame = pd.DataFrame(np.arange(12.).reshape((4, 3)), columns=list('bde'), index=['AA', 'BB', 'CC', 'DD'])>>> frame b d eAA 0.0 1.0 2.0BB 3.0 4.0 5.0CC 6.0 7.0 8.0DD 9.0 10.0 11.0>>> >>> series = frame.iloc[0]>>> seriesb 0.0d 1.0e 2.0Name: AA, dtype: float64>>> >>> frame - series b d eAA 0.0 0.0 0.0BB 3.0 3.0 3.0CC 6.0 6.0 6.0DD 9.0 9.0 9.0 如果某个索引值在 DataFrame 的列或 Series 的索引中找不到,则参与运算的两个对象就会被重新索引以形成并集: 1234567891011121314151617181920212223>>> import numpy as np>>> import pandas as pd>>> frame = pd.DataFrame(np.arange(12.).reshape((4, 3)), columns=list('bde'), index=['AA', 'BB', 'CC', 'DD'])>>> frame b d eAA 0.0 1.0 2.0BB 3.0 4.0 5.0CC 6.0 7.0 8.0DD 9.0 10.0 11.0>>> >>> series = pd.Series(range(3), index=['b', 'e', 'f'])>>> seriesb 0e 1f 2dtype: int64>>> >>> frame + series b d e fAA 0.0 NaN 3.0 NaNBB 3.0 NaN 6.0 NaNCC 6.0 NaN 9.0 NaNDD 9.0 NaN 12.0 NaN 如果希望匹配行且在列上广播,则必须使用算术运算方法,在方法中传入的轴(axis)就是希望匹配的轴。在下例中,我们的目的是匹配 DataFrame 的行索引(axis=’index’ or axis=0)并进行广播: 123456789101112131415161718192021222324>>> import numpy as np>>> import pandas as pd>>> frame = pd.DataFrame(np.arange(12.).reshape((4, 3)), columns=list('bde'), index=['AA', 'BB', 'CC', 'DD'])>>> frame b d eAA 0.0 1.0 2.0BB 3.0 4.0 5.0CC 6.0 7.0 8.0DD 9.0 10.0 11.0>>> >>> series = frame['d']>>> seriesAA 1.0BB 4.0CC 7.0DD 10.0Name: d, dtype: float64>>> >>> frame.sub(series, axis='index') b d eAA -1.0 0.0 1.0BB -1.0 0.0 1.0CC -1.0 0.0 1.0DD -1.0 0.0 1.0 【01x04】Pandas 算术方法完整的 Pandas 算术方法见下表: 方法 副本 描述 add() radd() 加法(+) sub()、subtract() rsub() 减法(-) mul()、multiply() rmul() 乘法(*) pow() rpow() 指数(**) truediv()、div()、divide() rdiv() 除法(/) floordiv() rfloordiv() 底除(//) mod() rmod() 求余(%) 副本均为原方法前加了个 r,它会翻转参数: 12345678910111213141516171819>>> import pandas as pd>>> obj = pd.DataFrame(np.arange(12.).reshape((3, 4)), columns=list('abcd'))>>> obj a b c d0 0.0 1.0 2.0 3.01 4.0 5.0 6.0 7.02 8.0 9.0 10.0 11.0>>> >>> 1 / obj a b c d0 inf 1.000000 0.500000 0.3333331 0.250 0.200000 0.166667 0.1428572 0.125 0.111111 0.100000 0.090909>>> >>> obj.rdiv(1) a b c d0 inf 1.000000 0.500000 0.3333331 0.250 0.200000 0.166667 0.1428572 0.125 0.111111 0.100000 0.090909 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106743778未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【02x00】处理缺失值在现实中遇到的数据很少是干净整齐的,许多数据集都会有数据缺失的现象,缺失值主要有三种形式:null、NaN(NAN,nan) 或 NA。 【02x01】fill_value() 指定值与缺失值进行运算使用 add, sub, div, mul 等算术方法时,通过 fill_value 指定填充值,未对齐的数据将和填充值做运算。 Series 中的应用: 1234567891011121314151617181920212223242526272829303132>>> import pandas as pd>>> obj1 = pd.Series([1, 2, 3, 4, 5])>>> obj2 = pd.Series([6, 7])>>> >>> obj10 11 22 33 44 5dtype: int64>>> >>> obj20 61 7dtype: int64>>> >>> obj1.add(obj2)0 7.01 9.02 NaN3 NaN4 NaNdtype: float64>>> >>> obj1.add(obj2, fill_value=-1)0 7.01 9.02 2.03 3.04 4.0dtype: float64 DataFrame 中的应用: 123456789101112131415161718192021222324252627282930313233>>> import pandas as pd>>> import numpy as np>>> obj1 = pd.DataFrame(np.arange(12.).reshape((3, 4)), columns=list('abcd'))>>> obj2 = pd.DataFrame(np.arange(20.).reshape((4, 5)), columns=list('abcde'))>>> >>> obj2.loc[1, 'b'] = np.nan>>> >>> obj1 a b c d0 0.0 1.0 2.0 3.01 4.0 5.0 6.0 7.02 8.0 9.0 10.0 11.0>>> >>> obj2 a b c d e0 0.0 1.0 2.0 3.0 4.01 5.0 NaN 7.0 8.0 9.02 10.0 11.0 12.0 13.0 14.03 15.0 16.0 17.0 18.0 19.0>>> >>> obj1 + obj2 a b c d e0 0.0 2.0 4.0 6.0 NaN1 9.0 NaN 13.0 15.0 NaN2 18.0 20.0 22.0 24.0 NaN3 NaN NaN NaN NaN NaN>>> >>> obj1.add(obj2, fill_value=10) a b c d e0 0.0 2.0 4.0 6.0 14.01 9.0 15.0 13.0 15.0 19.02 18.0 20.0 22.0 24.0 24.03 25.0 26.0 27.0 28.0 29.0 【02x02】isnull() / notnull() 判断缺失值isnull():为缺失值时为 True,否则为 False; notnull() 为缺失值时为 False,否则为 True。 1234567891011121314151617181920212223>>> import numpy as np>>> import pandas as pd>>> obj = pd.Series([1, np.nan, 'hello', None])>>> obj0 11 NaN2 hello3 Nonedtype: object>>> >>> obj.isnull()0 False1 True2 False3 Truedtype: bool>>> >>> obj.notnull()0 True1 False2 True3 Falsedtype: bool 【02x03】dropna() 删除缺失值dropna() 方法用于返回一个删除了缺失值的新 Series 或 DataFrame 对象。 在 Series 对象当中,dropna() 方法的语法如下(其他参数用法可参考在 DataFrame 中的应用): Series.dropna(self, axis=0, inplace=False, how=None) 官方文档:https://pandas.pydata.org/docs/reference/api/pandas.Series.dropna.html 1234567891011121314>>> import numpy as np>>> import pandas as pd>>> obj = pd.Series([1, np.nan, 'hello', None])>>> obj0 11 NaN2 hello3 Nonedtype: object>>> >>> obj.dropna()0 12 hellodtype: object 在 DataFrame 对象中,dropna() 方法的语法如下: DataFrame.dropna(self, axis=0, how='any', thresh=None, subset=None, inplace=False) 官方文档:https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.dropna.html 参数 描述 axis 确定是否删除包含缺失值的行或列0 或 'index':删除包含缺失值的行。1 或 'columns':删除包含缺失值的列 how 'any':如果存在任何NA值,则删除该行或列。'all':如果所有值都是NA,则删除该行或列 thresh 设置行或列中非缺失值的最小数量 不传递任何参数,将会删除任何包含缺失值的整行数据: 123456789101112>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame([[1, np.nan, 2], [2, 3, 5], [np.nan, 4, 6]])>>> obj 0 1 20 1.0 NaN 21 2.0 3.0 52 NaN 4.0 6>>> >>> obj.dropna() 0 1 21 2.0 3.0 5 指定 axis 参数,删除包含缺失值的行或列: 1234567891011121314>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame([[1, np.nan, 2], [2, 3, 5], [np.nan, 4, 6]])>>> obj 0 1 20 1.0 NaN 21 2.0 3.0 52 NaN 4.0 6>>> >>> obj.dropna(axis='columns') 20 21 52 6 指定 how 参数,'any':如果存在任何NA值,则删除该行或列。'all':如果所有值都是NA,则删除该行或列: 12345678910111213>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame([[1, np.nan, 2, np.nan], [2, 3, 5, np.nan], [np.nan, 4, 6, np.nan]])>>> obj 0 1 2 30 1.0 NaN 2 NaN1 2.0 3.0 5 NaN2 NaN 4.0 6 NaN>>> obj.dropna(axis='columns', how='all') 0 1 20 1.0 NaN 21 2.0 3.0 52 NaN 4.0 6 指定 thresh 参数,设置行或列中非缺失值的最小数量,以下示例中,第一行和第三行只有两个非缺失值,所以会被删除: 123456789101112>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame([[1, np.nan, 2, np.nan], [2, 3, 5, np.nan], [np.nan, 4, 6, np.nan]])>>> obj 0 1 2 30 1.0 NaN 2 NaN1 2.0 3.0 5 NaN2 NaN 4.0 6 NaN>>>>>> obj.dropna(axis='rows', thresh=3) 0 1 2 31 2.0 3.0 5 NaN 【02x04】fillna() 填充缺失值fillna() 方法可以将缺失值替换成有效的数值。 在 Series 对象中,fillna() 方法的语法如下: Series.fillna(self, value=None, method=None, axis=None, inplace=False, limit=None, downcast=None) 官方文档:https://pandas.pydata.org/docs/reference/api/pandas.Series.fillna.html 参数 描述 value 用于填充的值(例如 0),或者是一个 dict / Series / DataFrame 值指定要用于每个 index(对于 Series)或column(对于 DataFrame)的值不在dict / Series / DataFrame中的值将不被填充。此值不能是列表 method 填充方法:None‘pad’ / ‘ffill’:将上一个有效观测值向前传播到下一个有效观测值‘backfill’ / ‘bfill’:使用下一个有效观察值来填补空白 axis 0 or ‘index’,要填充缺失值的轴 123456789101112131415161718192021222324252627282930313233>>> import pandas as pd>>> obj = pd.Series([1, np.nan, 2, None, 3], index=list('abcde'))>>> obja 1.0b NaNc 2.0d NaNe 3.0dtype: float64>>> >>> obj.fillna(0)a 1.0b 0.0c 2.0d 0.0e 3.0dtype: float64>>> >>> obj.fillna(method='ffill')a 1.0b 1.0c 2.0d 2.0e 3.0dtype: float64>>> >>> obj.fillna(method='bfill')a 1.0b 2.0c 2.0d 3.0e 3.0dtype: float64 在 DataFrame 对象中,fillna() 方法的语法如下: DataFrame.fillna(self, value=None, method=None, axis=None, inplace=False, limit=None, downcast=None) 官方文档:https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.fillna.html 参数 描述 value 用于填充的值(例如 0),或者是一个 dict / Series / DataFrame 值指定要用于每个 index(对于 Series)或column(对于 DataFrame)的值不在dict / Series / DataFrame中的值将不被填充。此值不能是列表 method 填充方法:None‘pad’ / ‘ffill’:将上一个有效观测值向前传播到下一个有效观测值‘backfill’ / ‘bfill’:使用下一个有效观察值来填补空白 axis 0 or ‘index’,1 or ‘columns’,要填充缺失值的轴 在 DataFrame 对象中的用法和在 Series 对象中的用法大同小异,只不过 axis 参数多了一个选择: 1234567891011121314>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame([[1, np.nan, 2, np.nan], [2, 3, 5, np.nan], [np.nan, 4, 6, np.nan]])>>> obj 0 1 2 30 1.0 NaN 2 NaN1 2.0 3.0 5 NaN2 NaN 4.0 6 NaN>>> >>> obj.fillna(method='ffill', axis=1) 0 1 2 30 1.0 1.0 2.0 2.01 2.0 3.0 5.0 5.02 NaN 4.0 6.0 6.0 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106743778未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Pandas","slug":"Python-数据分析/Pandas","permalink":"https://www.itrhx.com/categories/Python-数据分析/Pandas/"}],"tags":[{"name":"Pandas","slug":"Pandas","permalink":"https://www.itrhx.com/tags/Pandas/"},{"name":"算术运算","slug":"算术运算","permalink":"https://www.itrhx.com/tags/算术运算/"},{"name":"缺失值","slug":"缺失值","permalink":"https://www.itrhx.com/tags/缺失值/"}]},{"title":"Python 数据分析三剑客之 Pandas(二):Index 索引对象以及各种索引操作","slug":"A80-Pandas-02","date":"2020-06-13T14:36:16.458Z","updated":"2020-07-06T13:44:26.916Z","comments":true,"path":"2020/06/13/A80-Pandas-02/","link":"","permalink":"https://www.itrhx.com/2020/06/13/A80-Pandas-02/","excerpt":"","text":"Pandas 系列文章: Python 数据分析三剑客之 Pandas(一):认识 Pandas 及其 Series、DataFrame 对象 Python 数据分析三剑客之 Pandas(二):Index 索引对象以及各种索引操作 Python 数据分析三剑客之 Pandas(三):算术运算与缺失值的处理 Python 数据分析三剑客之 Pandas(四):函数应用、映射、排序和层级索引 Python 数据分析三剑客之 Pandas(五):统计计算与统计描述 Python 数据分析三剑客之 Pandas(六):GroupBy 数据分裂、应用与合并 Python 数据分析三剑客之 Pandas(七):合并数据集 Python 数据分析三剑客之 Pandas(八):数据重塑、重复数据处理与数据替换 Python 数据分析三剑客之 Pandas(九):时间序列 Python 数据分析三剑客之 Pandas(十):数据读写 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106698307未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【1】Index 索引对象 Series 和 DataFrame 中的索引都是 Index 对象,为了保证数据的安全,索引对象是不可变的,如果尝试更改索引就会报错;常见的 Index 种类有:索引(Index),整数索引(Int64Index),层级索引(MultiIndex),时间戳类型(DatetimeIndex)。 一下代码演示了 Index 索引对象和其不可变的性质: 12345678910111213>>> import pandas as pd>>> obj = pd.Series([1, 5, -8, 2], index=['a', 'b', 'c', 'd'])>>> obj.indexIndex(['a', 'b', 'c', 'd'], dtype='object')>>> type(obj.index)<class 'pandas.core.indexes.base.Index'>>>> obj.index[0] = 'e'Traceback (most recent call last): File \"<pyshell#28>\", line 1, in <module> obj.index[0] = 'e' File \"C:\\Users\\...\\base.py\", line 3909, in __setitem__ raise TypeError(\"Index does not support mutable operations\")TypeError: Index does not support mutable operations index 索引对象常用属性 官方文档:https://pandas.pydata.org/docs/reference/api/pandas.Index.html 属性 描述 T 转置 array index 的数组形式,常见官方文档 dtype 返回基础数据的 dtype 对象 hasnans 是否有 NaN(缺失值) inferred_type 返回一个字符串,表示 index 的类型 is_monotonic 判断 index 是否是递增的 is_monotonic_decreasing 判断 index 是否单调递减 is_monotonic_increasing 判断 index 是否单调递增 is_unique index 是否没有重复值 nbytes 返回 index 中的字节数 ndim index 的维度 nlevels Number of levels. shape 返回一个元组,表示 index 的形状 size index 的大小 values 返回 index 中的值 / 数组 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748>>> import pandas as pd>>> obj = pd.Series([1, 5, -8, 2], index=['a', 'b', 'c', 'd'])>>> obj.indexIndex(['a', 'b', 'c', 'd'], dtype='object')>>> >>> obj.index.array<PandasArray>['a', 'b', 'c', 'd']Length: 4, dtype: object>>> >>> obj.index.dtypedtype('O')>>> >>> obj.index.hasnansFalse>>>>>> obj.index.inferred_type'string'>>> >>> obj.index.is_monotonicTrue>>>>>> obj.index.is_monotonic_decreasingFalse>>> >>> obj.index.is_monotonic_increasingTrue>>> >>> obj.index.is_uniqueTrue>>> >>> obj.index.nbytes16>>>>>> obj.index.ndim1>>>>>> obj.index.nlevels1>>>>>> obj.index.shape(4,)>>> >>> obj.index.size4>>> >>> obj.index.valuesarray(['a', 'b', 'c', 'd'], dtype=object) index 索引对象常用方法 官方文档:https://pandas.pydata.org/docs/reference/api/pandas.Index.html 方法 描述 all(self, *args, **kwargs) 判断所有元素是否为真,有 0 会被视为 False any(self, *args, **kwargs) 判断是否至少有一个元素为真,均为 0 会被视为 False append(self, other) 连接另一个 index,产生一个新的 index argmax(self[, axis, skipna]) 返回 index 中最大值的索引值 argmin(self[, axis, skipna]) 返回 index 中最小值的索引值 argsort(self, *args, **kwargs) 对 index 从小到大排序,返回排序后的元素在原 index 中的索引值 delete(self, loc) 删除指定索引位置的元素,返回删除后的新 index difference(self, other[, sort]) 在第一个 index 中删除第二个 index 中的元素,即差集 drop(self, labels[, errors]) 在原 index 中删除传入的值 drop_duplicates(self[, keep]) 删除重复值,keep 参数可选值如下:‘first’:保留第一次出现的重复项;‘last’:保留最后一次出现的重复项;False:不保留重复项 duplicated(self[, keep]) 判断是否为重复值,keep 参数可选值如下:‘first’:第一次重复的为 False,其他为 True;‘last’:最后一次重复的为 False,其他为 True;False:所有重复的均为 True dropna(self[, how]) 删除缺失值,即 NaN fillna(self[, value, downcast]) 用指定值填充缺失值,即 NaN equals(self, other) 判断两个 index 是否相同 insert(self, loc, item) 将元素插入到指定索引处,返回新的 index intersection(self, other[, sort]) 返回两个 index 的交集 isna(self) 检测 index 元素是否为缺失值,即 NaN isnull(self) 检测 index 元素是否为缺失值,即 NaN max(self[, axis, skipna]) 返回 index 的最大值 min(self[, axis, skipna]) 返回 index 的最小值 union(self, other[, sort]) 返回两个 index 的并集 unique(self[, level]) 返回 index 中的唯一值,相当于去除重复值 all(self, *args, **kwargs) 【官方文档】 123456>>> import pandas as pd>>> pd.Index([1, 2, 3]).all()True>>>>>> pd.Index([0, 1, 2]).all()False any(self, *args, **kwargs) 【官方文档】 123456>>> import pandas as pd>>> pd.Index([0, 0, 1]).any()True>>>>>> pd.Index([0, 0, 0]).any()False append(self, other) 【官方文档】 123>>> import pandas as pd>>> pd.Index(['a', 'b', 'c']).append(pd.Index([1, 2, 3]))Index(['a', 'b', 'c', 1, 2, 3], dtype='object') argmax(self[, axis, skipna]) 【官方文档】 123>>> import pandas as pd>>> pd.Index([5, 2, 3, 9, 1]).argmax()3 argmin(self[, axis, skipna]) 【官方文档】 123>>> import pandas as pd>>> pd.Index([5, 2, 3, 9, 1]).argmin()4 argsort(self, *args, **kwargs) 【官方文档】 123>>> import pandas as pd>>> pd.Index([5, 2, 3, 9, 1]).argsort()array([4, 1, 2, 0, 3], dtype=int32) delete(self, loc) 【官方文档】 123>>> import pandas as pd>>> pd.Index([5, 2, 3, 9, 1]).delete(0)Int64Index([2, 3, 9, 1], dtype='int64') difference(self, other[, sort]) 【官方文档】 1234567>>> import pandas as pd>>> idx1 = pd.Index([2, 1, 3, 4])>>> idx2 = pd.Index([3, 4, 5, 6])>>> idx1.difference(idx2)Int64Index([1, 2], dtype='int64')>>> idx1.difference(idx2, sort=False)Int64Index([2, 1], dtype='int64') drop(self, labels[, errors]) 【官方文档】 123>>> import pandas as pd>>> pd.Index([5, 2, 3, 9, 1]).drop([2, 1])Int64Index([5, 3, 9], dtype='int64') drop_duplicates(self[, keep]) 【官方文档】 12345678>>> import pandas as pd>>> idx = pd.Index(['lama', 'cow', 'lama', 'beetle', 'lama', 'hippo'])>>> idx.drop_duplicates(keep='first')Index(['lama', 'cow', 'beetle', 'hippo'], dtype='object')>>> idx.drop_duplicates(keep='last')Index(['cow', 'beetle', 'lama', 'hippo'], dtype='object')>>> idx.drop_duplicates(keep=False)Index(['cow', 'beetle', 'hippo'], dtype='object') duplicated(self[, keep]) 【官方文档】 12345678910>>> import pandas as pd>>> idx = pd.Index(['lama', 'cow', 'lama', 'beetle', 'lama'])>>> idx.duplicated()array([False, False, True, False, True])>>> idx.duplicated(keep='first')array([False, False, True, False, True])>>> idx.duplicated(keep='last')array([ True, False, True, False, False])>>> idx.duplicated(keep=False)array([ True, False, True, False, True]) dropna(self[, how]) 【官方文档】 1234>>> import numpy as np>>> import pandas as pd>>> pd.Index([2, 5, np.NaN, 6, np.NaN, np.NaN]).dropna()Float64Index([2.0, 5.0, 6.0], dtype='float64') fillna(self[, value, downcast]) 【官方文档】 1234>>> import numpy as np>>> import pandas as pd>>> pd.Index([2, 5, np.NaN, 6, np.NaN, np.NaN]).fillna(5)Float64Index([2.0, 5.0, 5.0, 6.0, 5.0, 5.0], dtype='float64') equals(self, other) 【官方文档】 12345678910>>> import pandas as pd>>> idx1 = pd.Index([5, 2, 3, 9, 1])>>> idx2 = pd.Index([5, 2, 3, 9, 1])>>> idx1.equals(idx2)True>>> >>> idx1 = pd.Index([5, 2, 3, 9, 1])>>> idx2 = pd.Index([5, 2, 4, 9, 1])>>> idx1.equals(idx2)False intersection(self, other[, sort]) 【官方文档】 12345>>> import pandas as pd>>> idx1 = pd.Index([1, 2, 3, 4])>>> idx2 = pd.Index([3, 4, 5, 6])>>> idx1.intersection(idx2)Int64Index([3, 4], dtype='int64') insert(self, loc, item) 【官方文档】 123>>> import pandas as pd>>> pd.Index([5, 2, 3, 9, 1]).insert(2, 'A')Index([5, 2, 'A', 3, 9, 1], dtype='object') isna(self) 【官方文档】、isnull(self) 【官方文档】 123456>>> import numpy as np>>> import pandas as pd>>> pd.Index([2, 5, np.NaN, 6, np.NaN, np.NaN]).isna()array([False, False, True, False, True, True])>>> pd.Index([2, 5, np.NaN, 6, np.NaN, np.NaN]).isnull()array([False, False, True, False, True, True]) max(self[, axis, skipna]) 【官方文档】、min(self[, axis, skipna]) 【官方文档】 12345>>> import pandas as pd>>> pd.Index([5, 2, 3, 9, 1]).max()9>>> pd.Index([5, 2, 3, 9, 1]).min()1 union(self, other[, sort]) 【官方文档】 12345>>> import pandas as pd>>> idx1 = pd.Index([1, 2, 3, 4])>>> idx2 = pd.Index([3, 4, 5, 6])>>> idx1.union(idx2)Int64Index([1, 2, 3, 4, 5, 6], dtype='int64') unique(self[, level]) 【官方文档】 123>>> import pandas as pd>>> pd.Index([5, 1, 3, 5, 1]).unique()Int64Index([5, 1, 3], dtype='int64') 【2】Pandas 一般索引由于在 Pandas 中,由于有一些更高级的索引操作,比如重新索引,层级索引等,因此将一般的切片索引、花式索引、布尔索引等归纳为一般索引。 【2.1】Series 索引【2.1.1】head() / tail()Series.head() 和 Series.tail() 方法可以获取的前五行和后五行数据,如果向 head() / tail() 里面传入参数,则会获取指定行: 1234567891011121314151617181920212223242526272829303132333435363738394041>>> import pandas as pd>>> import numpy as np>>> obj = pd.Series(np.random.randn(8))>>> obj0 -0.6434371 -0.3656522 -0.9665543 -0.0361274 1.0460955 -2.0483626 -1.8655517 1.344728dtype: float64>>> >>> obj.head()0 -0.6434371 -0.3656522 -0.9665543 -0.0361274 1.046095dtype: float64>>> >>> obj.head(3)0 -0.6434371 -0.3656522 -0.966554dtype: float64>>>>>> obj.tail()3 1.2212214 -1.3734965 1.0328436 0.0297347 -1.861485dtype: float64>>>>>> obj.tail(3)5 1.0328436 0.0297347 -1.861485dtype: float64 【2.1.2】行索引Pandas 中可以按照位置进行索引,也可以按照索引名(index)进行索引,也可以用 Python 字典的表达式和方法来获取值: 123456789101112131415161718>>> import pandas as pd>>> obj = pd.Series([1, 5, -8, 2], index=['a', 'b', 'c', 'd'])>>> obja 1b 5c -8d 2dtype: int64>>> obj['c']-8>>> obj[2]-8>>> 'b' in objTrue>>> obj.keys()Index(['a', 'b', 'c', 'd'], dtype='object')>>> list(obj.items())[('a', 1), ('b', 5), ('c', -8), ('d', 2)] 【2.1.3】切片索引切片的方法有两种:按位置切片和按索引名(index)切片,注意:按位置切片时,不包含终止索引;按索引名(index)切片时,包含终止索引。 123456789101112131415161718192021222324>>> import pandas as pd>>> obj = pd.Series([1, 5, -8, 2], index=['a', 'b', 'c', 'd'])>>> obja 1b 5c -8d 2dtype: int64>>>>>> obj[1:3]b 5c -8dtype: int64>>>>>> obj[0:3:2]a 1c -8dtype: int64>>>>>> obj['b':'d']b 5c -8d 2dtype: int64 【2.1.4】花式索引所谓的花式索引,就是间隔索引、不连续的索引,传递一个由索引名(index)或者位置参数组成的列表来一次性获得多个元素: 12345678910111213141516171819>>> import pandas as pd>>> obj = pd.Series([1, 5, -8, 2], index=['a', 'b', 'c', 'd'])>>> obja 1b 5c -8d 2dtype: int64>>> >>> obj[[0, 2]]a 1c -8dtype: int64>>> >>> obj[['a', 'c', 'd']]a 1c -8d 2dtype: int64 【2.1.5】布尔索引可以通过一个布尔数组来索引目标数组,即通过布尔运算(如:比较运算符)来获取符合指定条件的元素的数组。 1234567891011121314151617181920212223>>> import pandas as pd>>> obj = pd.Series([1, 5, -8, 2, -3], index=['a', 'b', 'c', 'd', 'e'])>>> obja 1b 5c -8d 2e -3dtype: int64>>> >>> obj[obj > 0]a 1b 5d 2dtype: int64>>> >>> obj > 0a Trueb Truec Falsed Truee Falsedtype: bool 【2.2】DataFrame 索引【2.2.1】head() / tail()和 Series 一样,DataFrame.head() 和 DataFrame.tail() 方法同样可以获取 DataFrame 的前五行和后五行数据,如果向 head() / tail() 里面传入参数,则会获取指定行: 1234567891011121314151617181920212223242526272829303132333435363738394041>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame(np.random.randn(8,4), columns = ['a', 'b', 'c', 'd'])>>> obj a b c d0 -1.399390 0.521596 -0.869613 0.5066211 -0.748562 -0.364952 0.188399 -1.4025662 1.378776 -1.476480 0.361635 0.4511343 -0.206405 -1.188609 3.002599 0.5636504 0.993289 1.133748 1.177549 -2.5622865 -0.482157 1.069293 1.143983 -1.3030796 -1.199154 0.220360 0.801838 -0.1045337 -1.359816 -2.092035 2.003530 -0.151812>>> >>> obj.head() a b c d0 -1.399390 0.521596 -0.869613 0.5066211 -0.748562 -0.364952 0.188399 -1.4025662 1.378776 -1.476480 0.361635 0.4511343 -0.206405 -1.188609 3.002599 0.5636504 0.993289 1.133748 1.177549 -2.562286>>> >>> obj.head(3) a b c d0 -1.399390 0.521596 -0.869613 0.5066211 -0.748562 -0.364952 0.188399 -1.4025662 1.378776 -1.476480 0.361635 0.451134>>>>>> obj.tail() a b c d3 -0.206405 -1.188609 3.002599 0.5636504 0.993289 1.133748 1.177549 -2.5622865 -0.482157 1.069293 1.143983 -1.3030796 -1.199154 0.220360 0.801838 -0.1045337 -1.359816 -2.092035 2.003530 -0.151812>>> >>> obj.tail(3) a b c d5 -0.482157 1.069293 1.143983 -1.3030796 -1.199154 0.220360 0.801838 -0.1045337 -1.359816 -2.092035 2.003530 -0.151812 【2.2.2】列索引DataFrame 可以按照列标签(columns)来进行列索引: 12345678910111213141516171819202122232425262728293031323334353637>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame(np.random.randn(7,2), columns = ['a', 'b'])>>> obj a b0 -1.198795 0.9283781 -2.878230 0.0146502 2.267475 0.3709523 0.639340 -1.3010414 -1.953444 0.1489345 -0.445225 0.4596326 0.097109 -2.592833>>>>>> obj['a']0 -1.1987951 -2.8782302 2.2674753 0.6393404 -1.9534445 -0.4452256 0.097109Name: a, dtype: float64>>> >>> obj[['a']] a0 -1.1987951 -2.8782302 2.2674753 0.6393404 -1.9534445 -0.4452256 0.097109>>> >>> type(obj['a'])<class 'pandas.core.series.Series'>>>> type(obj[['a']])<class 'pandas.core.frame.DataFrame'> 【2.2.3】切片索引DataFrame 中的切片索引是针对行来操作的,切片的方法有两种:按位置切片和按索引名(index)切片,注意:按位置切片时,不包含终止索引;按索引名(index)切片时,包含终止索引。 123456789101112131415161718192021222324252627282930>>> import pandas as pd>>> import numpy as np>>> data = np.random.randn(5,4)>>> index = ['I1', 'I2', 'I3', 'I4', 'I5']>>> columns = ['a', 'b', 'c', 'd']>>> obj = pd.DataFrame(data, index, columns)>>> obj a b c dI1 0.828676 -1.663337 1.753632 1.432487I2 0.368138 0.222166 0.902764 -1.436186I3 2.285615 -2.415175 -1.344456 -0.502214I4 3.224288 -0.500268 1.293596 -1.235549I5 -0.938833 -0.804433 -0.170047 -0.566766>>> >>> obj[0:3] a b c dI1 0.828676 -1.663337 1.753632 1.432487I2 0.368138 0.222166 0.902764 -1.436186I3 2.285615 -2.415175 -1.344456 -0.502214>>>>>> obj[0:4:2] a b c dI1 -0.042168 1.437354 -1.114545 0.830790I3 0.241506 0.018984 -0.499151 -1.190143>>>>>> obj['I2':'I4'] a b c dI2 0.368138 0.222166 0.902764 -1.436186I3 2.285615 -2.415175 -1.344456 -0.502214I4 3.224288 -0.500268 1.293596 -1.235549 【2.2.4】花式索引和 Series 一样,所谓的花式索引,就是间隔索引、不连续的索引,传递一个由列名(columns)组成的列表来一次性获得多列元素: 123456789101112131415161718192021>>> import pandas as pd>>> import numpy as np>>> data = np.random.randn(5,4)>>> index = ['I1', 'I2', 'I3', 'I4', 'I5']>>> columns = ['a', 'b', 'c', 'd']>>> obj = pd.DataFrame(data, index, columns)>>> obj a b c dI1 -1.083223 -0.182874 -0.348460 -1.572120I2 -0.205206 -0.251931 1.180131 0.847720I3 -0.980379 0.325553 -0.847566 -0.882343I4 -0.638228 -0.282882 -0.624997 -0.245980I5 -0.229769 1.002930 -0.226715 -0.916591>>> >>> obj[['a', 'd']] a dI1 -1.083223 -1.572120I2 -0.205206 0.847720I3 -0.980379 -0.882343I4 -0.638228 -0.245980I5 -0.229769 -0.916591 【2.2.5】布尔索引可以通过一个布尔数组来索引目标数组,即通过布尔运算(如:比较运算符)来获取符合指定条件的元素的数组。 1234567891011121314151617181920212223242526272829>>> import pandas as pd>>> import numpy as np>>> data = np.random.randn(5,4)>>> index = ['I1', 'I2', 'I3', 'I4', 'I5']>>> columns = ['a', 'b', 'c', 'd']>>> obj = pd.DataFrame(data, index, columns)>>> obj a b c dI1 -0.602984 -0.135716 0.999689 -0.339786I2 0.911130 -0.092485 -0.914074 -0.279588I3 0.849606 -0.420055 -1.240389 -0.179297I4 0.249986 -1.250668 0.329416 -1.105774I5 -0.743816 0.430647 -0.058126 -0.337319>>> >>> obj[obj > 0] a b c dI1 NaN NaN 0.999689 NaNI2 0.911130 NaN NaN NaNI3 0.849606 NaN NaN NaNI4 0.249986 NaN 0.329416 NaNI5 NaN 0.430647 NaN NaN>>> >>> obj > 0 a b c dI1 False False True FalseI2 True False False FalseI3 True False False FalseI4 True False True FalseI5 False True False False 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106698307未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【3】索引器:loc 和 ilocloc 是标签索引、iloc 是位置索引,注意:在 Pandas1.0.0 之前还有 ix 方法(即可按标签也可按位置索引),在 Pandas1.0.0 之后已被移除。 【3.1】loc 标签索引loc 标签索引,即根据 index 和 columns 来选择数据。 【3.1.1】Series.loc在 Series 中,允许输入: 单个标签,例如 5 或 'a',(注意,5 是 index 的名称,而不是位置索引); 标签列表或数组,例如 ['a', 'b', 'c']; 带有标签的切片对象,例如 'a':'f'。 官方文档:https://pandas.pydata.org/docs/reference/api/pandas.Series.loc.html 12345678910111213141516171819202122>>> import pandas as np>>> obj = pd.Series([1, 5, -8, 2], index=['a', 'b', 'c', 'd'])>>> obja 1b 5c -8d 2dtype: int64>>> >>> obj.loc['a']1>>> >>> obj.loc['a':'c']a 1b 5c -8dtype: int64>>>>>> obj.loc[['a', 'd']]a 1d 2dtype: int64 【3.1.2】DataFrame.loc在 DataFrame 中,第一个参数索引行,第二个参数是索引列,允许输入的格式和 Series 大同小异。 官方文档:https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.loc.html 1234567891011121314151617181920212223242526272829303132>>> import pandas as pd>>> obj = pd.DataFrame([[1, 2, 3], [4, 5, 6], [7, 8, 9]], index=['a', 'b', 'c'], columns=['A', 'B', 'C'])>>> obj A B Ca 1 2 3b 4 5 6c 7 8 9>>> >>> obj.loc['a']A 1B 2C 3Name: a, dtype: int64>>> >>> obj.loc['a':'c'] A B Ca 1 2 3b 4 5 6c 7 8 9>>> >>> obj.loc[['a', 'c']] A B Ca 1 2 3c 7 8 9>>> >>> obj.loc['b', 'B']5>>> obj.loc['b', 'A':'C']A 4B 5C 6Name: b, dtype: int64 【3.2】iloc 位置索引作用和 loc 一样,不过是基于索引的编号来索引,即根据 index 和 columns 的位置编号来选择数据。 【3.2.1】Series.iloc官方文档:https://pandas.pydata.org/docs/reference/api/pandas.Series.iloc.html 在 Series 中,允许输入: 整数,例如 5; 整数列表或数组,例如 [4, 3, 0]; 具有整数的切片对象,例如 1:7。 12345678910111213141516171819202122>>> import pandas as np>>> obj = pd.Series([1, 5, -8, 2], index=['a', 'b', 'c', 'd'])>>> obja 1b 5c -8d 2dtype: int64>>> >>> obj.iloc[1]5>>> >>> obj.iloc[0:2]a 1b 5dtype: int64>>> >>> obj.iloc[[0, 1, 3]]a 1b 5d 2dtype: int64 【3.2.2】DataFrame.iloc官方文档:https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.iloc.html 在 DataFrame 中,第一个参数索引行,第二个参数是索引列,允许输入的格式和 Series 大同小异: 12345678910111213141516171819202122232425262728293031>>> import pandas as pd>>> obj = pd.DataFrame([[1, 2, 3], [4, 5, 6], [7, 8, 9]], index=['a', 'b', 'c'], columns=['A', 'B', 'C'])>>> obj A B Ca 1 2 3b 4 5 6c 7 8 9>>> >>> obj.iloc[1]A 4B 5C 6Name: b, dtype: int64>>> >>> obj.iloc[0:2] A B Ca 1 2 3b 4 5 6>>> >>> obj.iloc[[0, 2]] A B Ca 1 2 3c 7 8 9>>> >>> obj.iloc[1, 2]6>>> >>> obj.iloc[1, 0:2]A 4B 5Name: b, dtype: int64 【4】Pandas 重新索引Pandas 对象的一个重要方法是 reindex,其作用是创建一个新对象,它的数据符合新的索引。以 DataFrame.reindex 为例(Series 类似),基本语法如下: DataFrame.reindex(self, labels=None, index=None, columns=None, axis=None, method=None, copy=True, level=None, fill_value=nan, limit=None, tolerance=None) 部分参数描述如下:(完整参数解释参见官方文档) 参数 描述 index 用作索引的新序列,既可以是 index 实例,也可以是其他序列型的 Python 数据结构 method 插值(填充)方式,取值如下:None:不填补空白;pad / ffill:将上一个有效的观测值向前传播到下一个有效的观测值;backfill / bfill:使用下一个有效观察值来填补空白;nearest:使用最近的有效观测值来填补空白。 fill_value 在重新索引的过程中,需要引入缺失值时使用的替代值 limit 前向或后向填充时的最大填充量 tolerance 向前或向后填充时,填充不准确匹配项的最大间距(绝对值距离) level 在 Multilndex 的指定级别上匹配简单索引,否则选其子集 copy 默认为 True,无论如何都复制;如果为 False,则新旧相等就不复制 reindex 将会根据新索引进行重排。如果某个索引值当前不存在,就引入缺失值: 1234567891011121314151617>>> import pandas as pd>>> obj = pd.Series([4.5, 7.2, -5.3, 3.6], index=['d', 'b', 'a', 'c'])>>> objd 4.5b 7.2a -5.3c 3.6dtype: float64>>> >>> obj2 = obj.reindex(['a', 'b', 'c', 'd', 'e'])>>> obj2a -5.3b 7.2c 3.6d 4.5e NaNdtype: float64 对于时间序列这样的有序数据,重新索引时可能需要做一些插值处理。method 选项即可达到此目的,例如,使用 ffill 可以实现前向值填充: 1234567891011121314151617>>> import pandas as pd>>> obj = pd.Series(['blue', 'purple', 'yellow'], index=[0, 2, 4])>>> obj0 blue2 purple4 yellowdtype: object>>> >>> obj2 = obj.reindex(range(6), method='ffill')>>> obj20 blue1 blue2 purple3 purple4 yellow5 yellowdtype: object 借助 DataFrame,reindex可以修改(行)索引和列。只传递一个序列时,会重新索引结果的行: 12345678910111213141516>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame(np.arange(9).reshape((3, 3)), index=['a', 'c', 'd'], columns=['Ohio', 'Texas', 'California'])>>> obj Ohio Texas Californiaa 0 1 2c 3 4 5d 6 7 8>>> >>> obj2 = obj.reindex(['a', 'b', 'c', 'd'])>>> obj2 Ohio Texas Californiaa 0.0 1.0 2.0b NaN NaN NaNc 3.0 4.0 5.0d 6.0 7.0 8.0 列可以用 columns 关键字重新索引: 123456789101112131415>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame(np.arange(9).reshape((3, 3)), index=['a', 'c', 'd'], columns=['Ohio', 'Texas', 'California'])>>> obj Ohio Texas Californiaa 0 1 2c 3 4 5d 6 7 8>>> >>> states = ['Texas', 'Utah', 'California']>>> obj.reindex(columns=states) Texas Utah Californiaa 1 NaN 2c 4 NaN 5d 7 NaN 8 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106698307未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Pandas","slug":"Python-数据分析/Pandas","permalink":"https://www.itrhx.com/categories/Python-数据分析/Pandas/"}],"tags":[{"name":"索引","slug":"索引","permalink":"https://www.itrhx.com/tags/索引/"},{"name":"Pandas","slug":"Pandas","permalink":"https://www.itrhx.com/tags/Pandas/"},{"name":"Index","slug":"Index","permalink":"https://www.itrhx.com/tags/Index/"}]},{"title":"Python 数据分析三剑客之 Pandas(一):认识 Pandas 及其 Series、DataFrame 对象","slug":"A79-Pandas-01","date":"2020-06-11T12:46:27.848Z","updated":"2020-08-06T03:30:39.925Z","comments":true,"path":"2020/06/11/A79-Pandas-01/","link":"","permalink":"https://www.itrhx.com/2020/06/11/A79-Pandas-01/","excerpt":"","text":"Pandas 系列文章: Python 数据分析三剑客之 Pandas(一):认识 Pandas 及其 Series、DataFrame 对象 Python 数据分析三剑客之 Pandas(二):Index 索引对象以及各种索引操作 Python 数据分析三剑客之 Pandas(三):算术运算与缺失值的处理 Python 数据分析三剑客之 Pandas(四):函数应用、映射、排序和层级索引 Python 数据分析三剑客之 Pandas(五):统计计算与统计描述 Python 数据分析三剑客之 Pandas(六):GroupBy 数据分裂、应用与合并 Python 数据分析三剑客之 Pandas(七):合并数据集 Python 数据分析三剑客之 Pandas(八):数据重塑、重复数据处理与数据替换 Python 数据分析三剑客之 Pandas(九):时间序列 Python 数据分析三剑客之 Pandas(十):数据读写 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106676693未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【01x00】了解 PandasPandas 是 Python 的一个数据分析包,是基于 NumPy 构建的,最初由 AQR Capital Management 于 2008 年 4 月开发,并于 2009 年底开源出来,目前由专注于 Python 数据包开发的 PyData 开发团队继续开发和维护,属于 PyData 项目的一部分。 Pandas 最初被作为金融数据分析工具而开发出来,因此,Pandas 为时间序列分析提供了很好的支持。Pandas 的名称来自于面板数据(panel data)和 Python 数据分析(data analysis)。panel data 是经济学中关于多维数据集的一个术语,在 Pandas 中也提供了 panel 的数据类型。 Pandas 经常和其它工具一同使用,如数值计算工具 NumPy 和 SciPy,分析库 statsmodels 和 scikit-learn,数据可视化库 Matplotlib 等,虽然 Pandas 采用了大量的 NumPy 编码风格,但二者最大的不同是 Pandas 是专门为处理表格和混杂数据设计的。而 NumPy 更适合处理统一的数值数组数据。 【以下对 Pandas 的解释翻译自官方文档:https://pandas.pydata.org/docs/getting_started/overview.html#package-overview】 Pandas 是 Python 的核心数据分析支持库,提供了快速、灵活、明确的数据结构,旨在简单、直观地处理关系型、标记型数据。Pandas 的目标是成为 Python 数据分析实践与实战的必备高级工具,其长远目标是成为最强大、最灵活、可以支持任何语言的开源数据分析工具。经过多年不懈的努力,Pandas 离这个目标已经越来越近了。 Pandas 适用于处理以下类型的数据: 与 SQL 或 Excel 表类似的,含异构列的表格数据; 有序和无序(非固定频率)的时间序列数据; 带行列标签的矩阵数据,包括同构或异构型数据; 任意其它形式的观测、统计数据集, 数据转入 Pandas 数据结构时不必事先标记。 Pandas 的主要数据结构是 Series(一维数据)与 DataFrame(二维数据),这两种数据结构足以处理- 金融、统计、社会科学、工程等领域里的大多数典型用例。对于 R 语言用户,DataFrame 提供了比 R 语言 data.frame 更丰富的功能。Pandas 基于 NumPy 开发,可以与其它第三方科学计算支持库完美集成。 Pandas 就像一把万能瑞士军刀,下面仅列出了它的部分优势 : 处理浮点与非浮点数据里的缺失数据,表示为 NaN; 大小可变:插入或删除 DataFrame 等多维对象的列; 自动、显式数据对齐:显式地将对象与一组标签对齐,也可以忽略标签,在 Series、DataFrame 计算时自动与数据对齐; 强大、灵活的分组(group by)功能:拆分-应用-组合数据集,聚合、转换数据; 把 Python 和 NumPy 数据结构里不规则、不同索引的数据轻松地转换为 DataFrame 对象; 基于智能标签,对大型数据集进行切片、花式索引、子集分解等操作; 直观地合并和连接数据集; 灵活地重塑和旋转数据集; 轴支持分层标签(每个刻度可能有多个标签); 强大的 IO 工具,读取平面文件(CSV 等支持分隔符的文件)、Excel 文件、数据库等来源的数据,以及从超快 HDF5 格式保存 / 加载数据; 时间序列:支持日期范围生成、频率转换、移动窗口统计、移动窗口线性回归、日期位移等时间序列功能。 这些功能主要是为了解决其它编程语言、科研环境的痛点。处理数据一般分为几个阶段:数据整理与清洗、数据分析与建模、数据可视化与制表,Pandas 是处理数据的理想工具。 其它说明: Pandas 速度很快。Pandas 的很多底层算法都用 Cython 优化过。然而,为了保持通用性,必然要牺牲一些性能,如果专注某一功能,完全可以开发出比 Pandas 更快的专用工具。 Pandas 是 statsmodels 的依赖项,因此,Pandas 也是 Python 中统计计算生态系统的重要组成部分。 Pandas 已广泛应用于金融领域。 【02x00】Pandas 数据结构Pandas 的主要数据结构是 Series(带标签的一维同构数组)与 DataFrame(带标签的,大小可变的二维异构表格)。 Pandas 数据结构就像是低维数据的容器。比如,DataFrame 是 Series 的容器,Series 则是标量的容器。使用这种方式,可以在容器中以字典的形式插入或删除对象。 此外,通用 API 函数的默认操作要顾及时间序列与截面数据集的方向。当使用 Ndarray 存储二维或三维数据时,编写函数要注意数据集的方向,这对用户来说是一种负担;如果不考虑 C 或 Fortran 中连续性对性能的影响,一般情况下,不同的轴在程序里其实没有什么区别。Pandas 里,轴的概念主要是为了给数据赋予更直观的语义,即用更恰当的方式表示数据集的方向。这样做可以让用户编写数据转换函数时,少费点脑子。 处理 DataFrame 等表格数据时,对比 Numpy,index(行)或 columns(列)比 axis 0 和 axis 1 更直观。用这种方式迭代 DataFrame 的列,代码更易读易懂: 123for col in df.columns: series = df[col] # do something with series 【03x00】Series 对象Series 是带标签的一维数组,可存储整数、浮点数、字符串、Python 对象等类型的数据。轴标签统称为索引。调用 pandas.Series 函数即可创建 Series,基本语法如下: pandas.Series(data=None[, index=None, dtype=None, name=None, copy=False, fastpath=False]) 参数 描述 data 数组类型,可迭代的,字典或标量值,存储在序列中的数据 index 索引(数据标签),值必须是可哈希的,并且具有与数据相同的长度,允许使用非唯一索引值。如果未提供,将默认为RangeIndex(0,1,2,…,n) dtype 输出系列的数据类型。可选项,如果未指定,则将从数据中推断,具体参考官网 dtypes 介绍 name str 类型,可选项,给 Series 命名 copy bool 类型,可选项,默认 False,是否复制输入数据 【03x01】通过 list 构建 Series一般情况下我们只会用到 data 和 index 参数,可以通过 list(列表) 构建 Series,示例如下: 12345678>>> import pandas as pd>>> obj = pd.Series([1, 5, -8, 2])>>> obj0 11 52 -83 2dtype: int64 由于我们没有为数据指定索引,于是会自动创建一个 0 到 N-1(N 为数据的长度)的整数型索引,左边一列是自动创建的索引(index),右边一列是数据(data)。 此外,还可以自定义索引(index): 12345678>>> import pandas as pd>>> obj = pd.Series([1, 5, -8, 2], index=['a', 'b', 'c', 'd'])>>> obja 1b 5c -8d 2dtype: int64 索引(index)也可以通过赋值的方式就地修改: 123456789101112131415>>> import pandas as pd>>> obj = pd.Series([1, 5, -8, 2], index=['a', 'b', 'c', 'd'])>>> obja 1b 5c -8d 2dtype: int64>>> obj.index = ['Bob', 'Steve', 'Jeff', 'Ryan']>>> objBob 1Steve 5Jeff -8Ryan 2dtype: int64 【03x02】通过 dict 构建 Series通过 字典(dict) 构建 Series,字典的键(key)会作为索引(index),字典的值(value)会作为数据(data),示例如下: 123456789>>> import pandas as pd>>> data = {'Beijing': 21530000, 'Shanghai': 24280000, 'Wuhan': 11210000, 'Zhejiang': 58500000}>>> obj = pd.Series(data)>>> objBeijing 21530000Shanghai 24280000Wuhan 11210000Zhejiang 58500000dtype: int64 如果你想按照某个特定的顺序输出结果,可以传入排好序的字典的键以改变顺序: 1234567891011>>> import pandas as pd>>> data = {'Beijing': 21530000, 'Shanghai': 24280000, 'Wuhan': 11210000, 'Zhejiang': 58500000}>>> cities = ['Guangzhou', 'Wuhan', 'Zhejiang', 'Shanghai']>>> obj = pd.Series(data, index=cities)>>> objGuangzhou NaNWuhan 11210000.0Zhejiang 58500000.0Shanghai 24280000.0dtype: float64 注意:data 为字典,且未设置 index 参数时: 如果 Python >= 3.6 且 Pandas >= 0.23,Series 按字典的插入顺序排序索引。 如果 Python < 3.6 或 Pandas < 0.23,Series 按字母顺序排序索引。 【03x03】获取其数据和索引我们可以通过 Series 的 values 和 index 属性获取其数据和索引对象: 123456>>> import pandas as pd>>> obj = pd.Series([1, 5, -8, 2], index=['a', 'b', 'c', 'd'])>>> obj.valuesarray([ 1, 5, -8, 2], dtype=int64)>>> obj.indexIndex(['a', 'b', 'c', 'd'], dtype='object') 【03x04】通过索引获取数据与普通 NumPy 数组相比,Pandas 可以通过索引的方式选取 Series 中的单个或一组值,获取一组值时,传入的是一个列表,列表中的元素是索引值,另外还可以通过索引来修改其对应的值: 12345678910111213141516>>> import pandas as pd>>> obj = pd.Series([1, 5, -8, 2], index=['a', 'b', 'c', 'd'])>>> obja 1b 5c -8d 2dtype: int64>>> obj['a']1>>> obj['a'] = 3>>> obj[['a', 'b', 'c']]a 3b 5c -8dtype: int64 【03x05】使用函数运算在 Pandas 中可以使用 NumPy 函数或类似 NumPy 的运算(如根据布尔型数组进行过滤、标量乘法、应用数学函数等): 1234567891011121314151617181920>>> import pandas as pd>>> import numpy as np>>> obj = pd.Series([1, 5, -8, 2], index=['a', 'b', 'c', 'd'])>>> obj[obj > 0]a 1b 5d 2dtype: int64>>> obj * 2a 2b 10c -16d 4dtype: int64>>> np.exp(obj)a 2.718282b 148.413159c 0.000335d 7.389056dtype: float64 除了这些运算函数以外,还可以将 Series 看成是一个定长的有序字典,因为它是索引值到数据值的一个映射。它可以用在许多原本需要字典参数的函数中: 123456>>> import pandas as pd>>> obj = pd.Series([1, 5, -8, 2], index=['a', 'b', 'c', 'd'])>>> 'a' in objTrue>>> 'e' in objFalse 和 NumPy 类似,Pandas 中也有 NaN(即非数字,not a number),在 Pandas 中,它用于表示缺失值,Pandas 的 isnull 和 notnull 函数可用于检测缺失数据: 123456789101112131415161718192021222324252627282930313233>>> import pandas as pd>>> import numpy as np>>> obj = pd.Series([np.NaN, 5, -8, 2], index=['a', 'b', 'c', 'd'])>>> obja NaNb 5.0c -8.0d 2.0dtype: float64>>> pd.isnull(obj)a Trueb Falsec Falsed Falsedtype: bool>>> pd.notnull(obj)a Falseb Truec Trued Truedtype: bool>>> obj.isnull()a Trueb Falsec Falsed Falsedtype: bool>>> obj.notnull()a Falseb Truec Trued Truedtype: bool 【03x06】name 属性可以在 pandas.Series 方法中为 Series 对象指定一个 name: 123456789>>> import pandas as pd>>> data = {'Beijing': 21530000, 'Shanghai': 24280000, 'Wuhan': 11210000, 'Zhejiang': 58500000}>>> obj = pd.Series(data, name='population')>>> objBeijing 21530000Shanghai 24280000Wuhan 11210000Zhejiang 58500000Name: population, dtype: int64 也可以通过 name 和 index.name 属性为 Series 对象和其索引指定 name: 123456789101112>>> import pandas as pd>>> data = {'Beijing': 21530000, 'Shanghai': 24280000, 'Wuhan': 11210000, 'Zhejiang': 58500000}>>> obj = pd.Series(data)>>> obj.name = 'population'>>> obj.index.name = 'cities'>>> objcitiesBeijing 21530000Shanghai 24280000Wuhan 11210000Zhejiang 58500000Name: population, dtype: int64 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106676693未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【04x00】DataFrame 对象DataFrame 是一个表格型的数据结构,它含有一组有序的列,每列可以是不同的值类型(数值、字符串、布尔值等)。DataFrame 既有行索引也有列索引,它可以被看做由 Series 组成的字典(共用同一个索引)。DataFrame 中的数据是以一个或多个二维块存放的(而不是列表、字典或别的一维数据结构)。 类似多维数组/表格数据 (如Excel、R 语言中的 data.frame); 每列数据可以是不同的类型; 索引包括列索引和行索引 基本语法如下: pandas.DataFrame(data=None, index: Optional[Collection] = None, columns: Optional[Collection] = None, dtype: Union[str, numpy.dtype, ExtensionDtype, None] = None, copy: bool = False) 参数 描述 data ndarray 对象(结构化或同类的)、可迭代的或者字典形式,存储在序列中的数据 index 数组类型,索引(数据标签),如果未提供,将默认为 RangeIndex(0,1,2,…,n) columns 列标签。如果未提供,则将默认为 RangeIndex(0、1、2、…、n) dtype 输出系列的数据类型。可选项,如果未指定,则将从数据中推断,具体参考官网 dtypes 介绍 copy bool 类型,可选项,默认 False,是否复制输入数据,仅影响 DataFrame/2d ndarray 输入 【03x01】通过 ndarray 构建 DataFrame1234567891011121314151617>>> import numpy as np>>> import pandas as pd>>> data = np.random.randn(5,3)>>> dataarray([[-2.16231157, 0.44967198, -0.73131523], [ 1.18982913, 0.94670798, 0.82973421], [-1.57680831, -0.99732066, 0.96432 ], [-0.77483149, -1.23802881, 0.44061227], [ 1.77666419, 0.24931983, -1.12960153]])>>> obj = pd.DataFrame(data)>>> obj 0 1 20 -2.162312 0.449672 -0.7313151 1.189829 0.946708 0.8297342 -1.576808 -0.997321 0.9643203 -0.774831 -1.238029 0.4406124 1.776664 0.249320 -1.129602 指定索引(index)和列标签(columns),和 Series 对象类似,可以在构建的时候添加索引和标签,也可以直接通过赋值的方式就地修改: 1234567891011121314151617181920212223>>> import numpy as np>>> import pandas as pd>>> data = np.random.randn(5,3)>>> index = ['a', 'b', 'c', 'd', 'e']>>> columns = ['A', 'B', 'C']>>> obj = pd.DataFrame(data, index, columns)>>> obj A B Ca -1.042909 -0.238236 -1.050308b 0.587079 0.739683 -0.233624c -0.451254 -0.638496 1.708807d -0.620158 -1.875929 -0.432382e -1.093815 0.396965 -0.759479>>>>>> obj.index = ['A1', 'A2', 'A3', 'A4', 'A5']>>> obj.columns = ['B1', 'B2', 'B3']>>> obj B1 B2 B3A1 -1.042909 -0.238236 -1.050308A2 0.587079 0.739683 -0.233624A3 -0.451254 -0.638496 1.708807A4 -0.620158 -1.875929 -0.432382A5 -1.093815 0.396965 -0.759479 【03x02】通过 dict 构建 DataFrame通过 字典(dict) 构建 DataFrame,字典的键(key)会作为列标签(columns),字典的值(value)会作为数据(data),示例如下: 12345678910111213>>> import pandas as pd>>> data = {'city': ['Wuhan', 'Wuhan', 'Wuhan', 'Beijing', 'Beijing', 'Beijing'], 'year': [2017, 2018, 2019, 2017, 2018, 2019], 'people': [10892900, 11081000, 11212000, 21707000, 21542000, 21536000]}>>> obj = pd.DataFrame(data)>>> obj city year people0 Wuhan 2017 108929001 Wuhan 2018 110810002 Wuhan 2019 112120003 Beijing 2017 217070004 Beijing 2018 215420005 Beijing 2019 21536000 如果指定了列序列,则 DataFrame 的列就会按照指定顺序进行排列,如果传入的列在数据中找不到,就会在结果中产生缺失值(NaN): 12345678910111213141516171819202122232425262728>>> import pandas as pd>>> data = {'city': ['Wuhan', 'Wuhan', 'Wuhan', 'Beijing', 'Beijing', 'Beijing'], 'year': [2017, 2018, 2019, 2017, 2018, 2019], 'people': [10892900, 11081000, 11212000, 21707000, 21542000, 21536000]}>>> pd.DataFrame(data) city year people0 Wuhan 2017 108929001 Wuhan 2018 110810002 Wuhan 2019 112120003 Beijing 2017 217070004 Beijing 2018 215420005 Beijing 2019 21536000>>> pd.DataFrame(data, columns=['year', 'city', 'people']) year city people0 2017 Wuhan 108929001 2018 Wuhan 110810002 2019 Wuhan 112120003 2017 Beijing 217070004 2018 Beijing 215420005 2019 Beijing 21536000>>> pd.DataFrame(data, columns=['year', 'city', 'people', 'money']) year city people money0 2017 Wuhan 10892900 NaN1 2018 Wuhan 11081000 NaN2 2019 Wuhan 11212000 NaN3 2017 Beijing 21707000 NaN4 2018 Beijing 21542000 NaN5 2019 Beijing 21536000 NaN 注意:data 为字典,且未设置 columns 参数时: Python > = 3.6 且 Pandas > = 0.23,DataFrame 的列按字典的插入顺序排序。 Python < 3.6 或 Pandas < 0.23,DataFrame 的列按字典键的字母排序。 【03x03】获取其数据和索引和 Series 一样,DataFrame 也可以通过其 values 和 index 属性获取其数据和索引对象: 123456789101112131415>>> import numpy as np>>> import pandas as pd>>> data = {'city': ['Wuhan', 'Wuhan', 'Wuhan', 'Beijing', 'Beijing', 'Beijing'], 'year': [2017, 2018, 2019, 2017, 2018, 2019], 'people': [10892900, 11081000, 11212000, 21707000, 21542000, 21536000]}>>> obj = pd.DataFrame(data)>>> obj.indexRangeIndex(start=0, stop=6, step=1)>>> obj.valuesarray([['Wuhan', 2017, 10892900], ['Wuhan', 2018, 11081000], ['Wuhan', 2019, 11212000], ['Beijing', 2017, 21707000], ['Beijing', 2018, 21542000], ['Beijing', 2019, 21536000]], dtype=object) 【03x04】通过索引获取数据通过类似字典标记的方式或属性的方式,可以将 DataFrame 的列获取为一个 Series 对象; 行也可以通过位置或名称的方式进行获取,比如用 loc 属性; 对于特别大的 DataFrame,有一个 head 方法可以选取前五行数据。 用法示例: 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849>>> import numpy as np>>> import pandas as pd>>> data = {'city': ['Wuhan', 'Wuhan', 'Wuhan', 'Beijing', 'Beijing', 'Beijing'], 'year': [2017, 2018, 2019, 2017, 2018, 2019], 'people': [10892900, 11081000, 11212000, 21707000, 21542000, 21536000]}>>> obj = pd.DataFrame(data)>>> obj city year people0 Wuhan 2017 108929001 Wuhan 2018 110810002 Wuhan 2019 112120003 Beijing 2017 217070004 Beijing 2018 215420005 Beijing 2019 21536000>>>>>> obj['city']0 Wuhan1 Wuhan2 Wuhan3 Beijing4 Beijing5 BeijingName: city, dtype: object>>>>>> obj.year0 20171 20182 20193 20174 20185 2019Name: year, dtype: int64>>>>>> type(obj.year)<class 'pandas.core.series.Series'>>>>>>> obj.loc[2]city Wuhanyear 2019people 11212000Name: 2, dtype: object>>>>>> obj.head() city year people0 Wuhan 2017 108929001 Wuhan 2018 110810002 Wuhan 2019 112120003 Beijing 2017 217070004 Beijing 2018 21542000 【03x05】修改列的值列可以通过赋值的方式进行修改。在下面示例中,分别给”money”列赋上一个标量值和一组值: 1234567891011121314151617181920212223242526272829303132333435>>> import pandas as pd>>> import numpy as np>>> data = {'city': ['Wuhan', 'Wuhan', 'Wuhan', 'Beijing', 'Beijing', 'Beijing'], 'year': [2017, 2018, 2019, 2017, 2018, 2019], 'people': [10892900, 11081000, 11212000, 21707000, 21542000, 21536000], 'money':[np.NaN, np.NaN, np.NaN, np.NaN, np.NaN, np.NaN]}>>> obj = pd.DataFrame(data, index=['A', 'B', 'C', 'D', 'E', 'F'])>>> obj city year people moneyA Wuhan 2017 10892900 NaNB Wuhan 2018 11081000 NaNC Wuhan 2019 11212000 NaND Beijing 2017 21707000 NaNE Beijing 2018 21542000 NaNF Beijing 2019 21536000 NaN>>>>>> obj['money'] = 6666666666>>> obj city year people moneyA Wuhan 2017 10892900 6666666666B Wuhan 2018 11081000 6666666666C Wuhan 2019 11212000 6666666666D Beijing 2017 21707000 6666666666E Beijing 2018 21542000 6666666666F Beijing 2019 21536000 6666666666>>>>>> obj['money'] = np.arange(100000000, 700000000, 100000000)>>> obj city year people moneyA Wuhan 2017 10892900 100000000B Wuhan 2018 11081000 200000000C Wuhan 2019 11212000 300000000D Beijing 2017 21707000 400000000E Beijing 2018 21542000 500000000F Beijing 2019 21536000 600000000 将列表或数组赋值给某个列时,其长度必须跟 DataFrame 的长度相匹配。如果赋值的是一个 Series,就会精确匹配 DataFrame 的索引: 1234567891011121314151617181920212223242526>>> import pandas as pd>>> import numpy as np>>> data = {'city': ['Wuhan', 'Wuhan', 'Wuhan', 'Beijing', 'Beijing', 'Beijing'], 'year': [2017, 2018, 2019, 2017, 2018, 2019], 'people': [10892900, 11081000, 11212000, 21707000, 21542000, 21536000], 'money':[np.NaN, np.NaN, np.NaN, np.NaN, np.NaN, np.NaN]}>>> obj = pd.DataFrame(data, index=['A', 'B', 'C', 'D', 'E', 'F'])>>> obj city year people moneyA Wuhan 2017 10892900 NaNB Wuhan 2018 11081000 NaNC Wuhan 2019 11212000 NaND Beijing 2017 21707000 NaNE Beijing 2018 21542000 NaNF Beijing 2019 21536000 NaN>>> >>> new_data = pd.Series([5670000000, 6890000000, 7890000000], index=['A', 'C', 'E'])>>> obj['money'] = new_data>>> obj city year people moneyA Wuhan 2017 10892900 5.670000e+09B Wuhan 2018 11081000 NaNC Wuhan 2019 11212000 6.890000e+09D Beijing 2017 21707000 NaNE Beijing 2018 21542000 7.890000e+09F Beijing 2019 21536000 NaN 【03x06】增加 / 删除列为不存在的列赋值会创建出一个新列,关键字 del 用于删除列: 123456789101112131415161718192021222324252627282930313233>>> import pandas as pd>>> data = {'city': ['Wuhan', 'Wuhan', 'Wuhan', 'Beijing', 'Beijing', 'Beijing'], 'year': [2017, 2018, 2019, 2017, 2018, 2019], 'people': [10892900, 11081000, 11212000, 21707000, 21542000, 21536000]}>>> obj = pd.DataFrame(data)>>> obj city year people0 Wuhan 2017 108929001 Wuhan 2018 110810002 Wuhan 2019 112120003 Beijing 2017 217070004 Beijing 2018 215420005 Beijing 2019 21536000>>> >>> obj['northern'] = obj['city'] == 'Beijing'>>> obj city year people northern0 Wuhan 2017 10892900 False1 Wuhan 2018 11081000 False2 Wuhan 2019 11212000 False3 Beijing 2017 21707000 True4 Beijing 2018 21542000 True5 Beijing 2019 21536000 True>>> >>> del obj['northern']>>> obj city year people0 Wuhan 2017 108929001 Wuhan 2018 110810002 Wuhan 2019 112120003 Beijing 2017 217070004 Beijing 2018 215420005 Beijing 2019 21536000 【03x07】name 属性可以通过 index.name 和 columns.name 属性设置索引(index)和列标签(columns)的 name,注意 DataFrame 对象是没有 name 属性的: 12345678910111213141516>>> import pandas as pd>>> data = {'city': ['Wuhan', 'Wuhan', 'Wuhan', 'Beijing', 'Beijing', 'Beijing'], 'year': [2017, 2018, 2019, 2017, 2018, 2019], 'people': [10892900, 11081000, 11212000, 21707000, 21542000, 21536000]}>>> obj = pd.DataFrame(data)>>> obj.index.name = 'index'>>> obj.columns.name = 'columns'>>> objcolumns city year peopleindex 0 Wuhan 2017 108929001 Wuhan 2018 110810002 Wuhan 2019 112120003 Beijing 2017 217070004 Beijing 2018 215420005 Beijing 2019 21536000 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106676693未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Pandas","slug":"Python-数据分析/Pandas","permalink":"https://www.itrhx.com/categories/Python-数据分析/Pandas/"}],"tags":[{"name":"Pandas","slug":"Pandas","permalink":"https://www.itrhx.com/tags/Pandas/"},{"name":"Series","slug":"Series","permalink":"https://www.itrhx.com/tags/Series/"},{"name":"DataFrame","slug":"DataFrame","permalink":"https://www.itrhx.com/tags/DataFrame/"}]},{"title":"Python 数据分析三剑客之 Matplotlib(十一):最常用最有价值的 50 个图表【译文】","slug":"A78-Matplotlib-11","date":"2020-06-09T08:13:44.322Z","updated":"2020-08-06T03:30:09.138Z","comments":true,"path":"2020/06/09/A78-Matplotlib-11/","link":"","permalink":"https://www.itrhx.com/2020/06/09/A78-Matplotlib-11/","excerpt":"","text":"Matplotlib 系列文章: Python 数据分析三剑客之 Matplotlib(一):初识 Matplotlib 与其 matplotibrc 配置文件 Python 数据分析三剑客之 Matplotlib(二):文本描述 / 中文支持 / 画布 / 网格等基本图像属性 Python 数据分析三剑客之 Matplotlib(三):图例 / LaTeX / 刻度 / 子图 / 补丁等基本图像属性 Python 数据分析三剑客之 Matplotlib(四):线性图的绘制 Python 数据分析三剑客之 Matplotlib(五):散点图的绘制 Python 数据分析三剑客之 Matplotlib(六):直方图 / 柱状图 / 条形图的绘制 Python 数据分析三剑客之 Matplotlib(七):饼状图的绘制 Python 数据分析三剑客之 Matplotlib(八):等高线 / 等值线图的绘制 Python 数据分析三剑客之 Matplotlib(九):极区图 / 极坐标图 / 雷达图的绘制 Python 数据分析三剑客之 Matplotlib(十):3D 图的绘制 Python 数据分析三剑客之 Matplotlib(十一):最热门最常用的 50 个图表【译文】 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 翻译丨TRHX作者丨Selva Prabhakaran原文丨《Top 50 matplotlib Visualizations – The Master Plots (with full python code)》 ★ 本文中的示例原作者使用的编辑器为 Jupyter Notebook;★ 译者使用 PyCharm 测试原文中有部分代码不太准确,部分已进行修改,对应有注释说明;★ 运行本文代码,需要安装 Matplotlib 和 Seaborn 等可视化库,其他的一些辅助可视化库已在代码部分作标注;★ 示例中用到的数据均储存在作者的 GitHub:https://github.com/selva86/datasets,因此运行程序可能需要FQ;★ 译者英文水平有限,若遇到翻译模糊的词建议参考原文来理解。★ 本文50个示例代码已打包为 .py 文件,可直接下载:https://download.csdn.net/download/qq_36759224/12507219 1234这里是一段防爬虫文本,请读者忽略。本译文首发于 CSDN,作者 Selva Prabhakaran,译者 TRHX。本文链接:https://itrhx.blog.csdn.net/article/details/106558131原文链接:https://www.machinelearningplus.com/plots/top-50-matplotlib-visualizations-the-master-plots-python/ 【1x00】介绍(Introduction)在数据分析和可视化中最常用的、最有价值的前 50 个 Matplotlib 图表。这些图表会让你懂得在不同情况下合理使用 Python 的 Matplotlib 和 Seaborn 库来达到数据可视化效果。 这些图表根据可视化目标的 7 个不同情景进行分组。 例如,如果要想象两个变量之间的关系,请查看“关联”部分下的图表。 或者,如果您想要显示值如何随时间变化,请查看“变化”部分,依此类推。 有效图表的重要特征: 在不歪曲事实的情况下传达正确和必要的信息; 设计简单,不必太费力就能理解它; 从审美角度支持信息而不是掩盖信息; 信息没有超负荷。 【2x00】准备工作(Setup)在代码运行前先引入下面的基本设置,当然,个别图表可能会重新定义显示要素。 12345678910111213141516171819202122232425# !pip install brewer2mplimport numpy as npimport pandas as pdimport matplotlib as mplimport matplotlib.pyplot as pltimport seaborn as snsimport warnings; warnings.filterwarnings(action='once')large = 22; med = 16; small = 12params = {'axes.titlesize': large, 'legend.fontsize': med, 'figure.figsize': (16, 10), 'axes.labelsize': med, 'axes.titlesize': med, 'xtick.labelsize': med, 'ytick.labelsize': med, 'figure.titlesize': large}plt.rcParams.update(params)plt.style.use('seaborn-whitegrid')sns.set_style(\"white\")%matplotlib inline# Versionprint(mpl.__version__) #> 3.0.0print(sns.__version__) #> 0.9.0 【3x00】关联(Correlation) 关联图用于可视化两个或多个变量之间的关系。也就是说,一个变量相对于另一个变量如何变化。 【01】散点图(Scatter plot)散点图是研究两个变量之间关系的经典和基本的绘图。如果数据中有多个组,则可能需要以不同的颜色显示每个组。在 Matplotlib 中,您可以使用 plt.scatterplot() 方便地执行此操作。 12345678910111213141516171819202122232425# Import dataset midwest = pd.read_csv(\"https://raw.githubusercontent.com/selva86/datasets/master/midwest_filter.csv\")# Prepare Data # Create as many colors as there are unique midwest['category']categories = np.unique(midwest['category'])colors = [plt.cm.tab10(i/float(len(categories)-1)) for i in range(len(categories))]# Draw Plot for Each Categoryplt.figure(figsize=(16, 10), dpi= 80, facecolor='w', edgecolor='k')for i, category in enumerate(categories): plt.scatter('area', 'poptotal', data=midwest.loc[midwest.category==category, :], s=20, cmap=colors[i], label=str(category))# 原文 c=colors[i] 已修改为 cmap=colors[i]# Decorationsplt.gca().set(xlim=(0.0, 0.1), ylim=(0, 90000), xlabel='Area', ylabel='Population')plt.xticks(fontsize=12); plt.yticks(fontsize=12)plt.title(\"Scatterplot of Midwest Area vs Population\", fontsize=22)plt.legend(fontsize=12)plt.show() 【02】带边界的气泡图(Bubble plot with Encircling)有时候您想在一个边界内显示一组点来强调它们的重要性。在本例中,您将从被包围的数据中获取记录,并将其传递给下面的代码中描述的 encircle()。 12345678910111213141516171819202122232425262728293031323334353637383940414243from matplotlib import patchesfrom scipy.spatial import ConvexHullimport warnings; warnings.simplefilter('ignore')sns.set_style(\"white\")# Step 1: Prepare Datamidwest = pd.read_csv(\"https://raw.githubusercontent.com/selva86/datasets/master/midwest_filter.csv\")# As many colors as there are unique midwest['category']categories = np.unique(midwest['category'])colors = [plt.cm.tab10(i/float(len(categories)-1)) for i in range(len(categories))]# Step 2: Draw Scatterplot with unique color for each categoryfig = plt.figure(figsize=(16, 10), dpi=80, facecolor='w', edgecolor='k')for i, category in enumerate(categories): plt.scatter('area', 'poptotal', data=midwest.loc[midwest.category == category, :], s='dot_size', cmap=colors[i], label=str(category), edgecolors='black', linewidths=.5)# 原文 c=colors[i] 已修改为 cmap=colors[i]# Step 3: Encircling# https://stackoverflow.com/questions/44575681/how-do-i-encircle-different-data-sets-in-scatter-plotdef encircle(x,y, ax=None, **kw): if not ax: ax = plt.gca() p = np.c_[x, y] hull = ConvexHull(p) poly = plt.Polygon(p[hull.vertices, :], **kw) ax.add_patch(poly)# Select data to be encircledmidwest_encircle_data = midwest.loc[midwest.state=='IN', :]# Draw polygon surrounding verticesencircle(midwest_encircle_data.area, midwest_encircle_data.poptotal, ec=\"k\", fc=\"gold\", alpha=0.1)encircle(midwest_encircle_data.area, midwest_encircle_data.poptotal, ec=\"firebrick\", fc=\"none\", linewidth=1.5)# Step 4: Decorationsplt.gca().set(xlim=(0.0, 0.1), ylim=(0, 90000), xlabel='Area', ylabel='Population')plt.xticks(fontsize=12); plt.yticks(fontsize=12)plt.title(\"Bubble Plot with Encircling\", fontsize=22)plt.legend(fontsize=12)plt.show() 【03】带线性回归最佳拟合线的散点图(Scatter plot with linear regression line of best fit)如果你想了解两个变量之间是如何变化的,那么最佳拟合线就是常用的方法。下图显示了数据中不同组之间的最佳拟合线的差异。若要禁用分组并只为整个数据集绘制一条最佳拟合线,请从 sns.lmplot() 方法中删除 hue ='cyl' 参数。 1234567891011121314# Import Datadf = pd.read_csv(\"https://raw.githubusercontent.com/selva86/datasets/master/mpg_ggplot2.csv\")df_select = df.loc[df.cyl.isin([4, 8]), :]# Plotsns.set_style(\"white\")gridobj = sns.lmplot(x=\"displ\", y=\"hwy\", hue=\"cyl\", data=df_select, height=7, aspect=1.6, robust=True, palette='tab10', scatter_kws=dict(s=60, linewidths=.7, edgecolors='black'))# Decorationsgridobj.set(xlim=(0.5, 7.5), ylim=(0, 50))plt.title(\"Scatterplot with line of best fit grouped by number of cylinders\", fontsize=20)plt.show() 针对每一组数据绘制线性回归线(Each regression line in its own column),可以通过在 sns.lmplot() 中设置 col=groupingcolumn 参数来实现,如下: 1234567891011121314151617# Import Datadf = pd.read_csv(\"https://raw.githubusercontent.com/selva86/datasets/master/mpg_ggplot2.csv\")df_select = df.loc[df.cyl.isin([4, 8]), :]# Each line in its own columnsns.set_style(\"white\")gridobj = sns.lmplot(x=\"displ\", y=\"hwy\", data=df_select, height=7, robust=True, palette='Set1', col=\"cyl\", scatter_kws=dict(s=60, linewidths=.7, edgecolors='black'))# Decorationsgridobj.set(xlim=(0.5, 7.5), ylim=(0, 50))plt.show() 【04】抖动图(Jittering with stripplot)通常,多个数据点具有完全相同的 X 和 Y 值。 此时多个点绘制会重叠并隐藏。为避免这种情况,可以将数据点稍微抖动,以便可以直观地看到它们。 使用 seaborn 库的 stripplot() 方法可以很方便的实现这个功能。 12345678910# Import Datadf = pd.read_csv(\"https://raw.githubusercontent.com/selva86/datasets/master/mpg_ggplot2.csv\")# Draw Stripplotfig, ax = plt.subplots(figsize=(16,10), dpi= 80)sns.stripplot(df.cty, df.hwy, jitter=0.25, size=8, ax=ax, linewidth=.5)# Decorationsplt.title('Use jittered plots to avoid overlapping of points', fontsize=22)plt.show() 【05】计数图(Counts Plot)避免点重叠问题的另一个选择是根据点的位置增加点的大小。所以,点的大小越大,它周围的点就越集中。 1234567891011# Import Datadf = pd.read_csv(\"https://raw.githubusercontent.com/selva86/datasets/master/mpg_ggplot2.csv\")df_counts = df.groupby(['hwy', 'cty']).size().reset_index(name='counts')# Draw Stripplotfig, ax = plt.subplots(figsize=(16,10), dpi= 80) sns.stripplot(df_counts.cty, df_counts.hwy, size=df_counts.counts*2, ax=ax)# Decorationsplt.title('Counts Plot - Size of circle is bigger as more points overlap', fontsize=22)plt.show() 【06】边缘直方图(Marginal Histogram)边缘直方图是具有沿 X 和 Y 轴变量的直方图。 这用于可视化 X 和 Y 之间的关系以及单独的 X 和 Y 的单变量分布。 这种图经常用于探索性数据分析(EDA)。 12345678910111213141516171819202122232425262728293031# Import Datadf = pd.read_csv(\"https://raw.githubusercontent.com/selva86/datasets/master/mpg_ggplot2.csv\")# Create Fig and gridspecfig = plt.figure(figsize=(16, 10), dpi= 80)grid = plt.GridSpec(4, 4, hspace=0.5, wspace=0.2)# Define the axesax_main = fig.add_subplot(grid[:-1, :-1])ax_right = fig.add_subplot(grid[:-1, -1], xticklabels=[], yticklabels=[])ax_bottom = fig.add_subplot(grid[-1, 0:-1], xticklabels=[], yticklabels=[])# Scatterplot on main axax_main.scatter('displ', 'hwy', s=df.cty*4, c=df.manufacturer.astype('category').cat.codes, alpha=.9, data=df, cmap=\"tab10\", edgecolors='gray', linewidths=.5)# histogram on the rightax_bottom.hist(df.displ, 40, histtype='stepfilled', orientation='vertical', color='deeppink')ax_bottom.invert_yaxis()# histogram in the bottomax_right.hist(df.hwy, 40, histtype='stepfilled', orientation='horizontal', color='deeppink')# Decorationsax_main.set(title='Scatterplot with Histograms \\n displ vs hwy', xlabel='displ', ylabel='hwy')ax_main.title.set_fontsize(20)for item in ([ax_main.xaxis.label, ax_main.yaxis.label] + ax_main.get_xticklabels() + ax_main.get_yticklabels()): item.set_fontsize(14)xlabels = ax_main.get_xticks().tolist()ax_main.set_xticklabels(xlabels)plt.show() 【07】边缘箱形图(Marginal Boxplot)边缘箱形图与边缘直方图具有相似的用途。 然而,箱线图有助于精确定位 X 和 Y 的中位数、第25和第75百分位数。 123456789101112131415161718192021222324252627282930313233# Import Datadf = pd.read_csv(\"https://raw.githubusercontent.com/selva86/datasets/master/mpg_ggplot2.csv\")# Create Fig and gridspecfig = plt.figure(figsize=(16, 10), dpi= 80)grid = plt.GridSpec(4, 4, hspace=0.5, wspace=0.2)# Define the axesax_main = fig.add_subplot(grid[:-1, :-1])ax_right = fig.add_subplot(grid[:-1, -1], xticklabels=[], yticklabels=[])ax_bottom = fig.add_subplot(grid[-1, 0:-1], xticklabels=[], yticklabels=[])# Scatterplot on main axax_main.scatter('displ', 'hwy', s=df.cty*5, c=df.manufacturer.astype('category').cat.codes, alpha=.9, data=df, cmap=\"Set1\", edgecolors='black', linewidths=.5)# Add a graph in each partsns.boxplot(df.hwy, ax=ax_right, orient=\"v\")sns.boxplot(df.displ, ax=ax_bottom, orient=\"h\")# Decorations ------------------# Remove x axis name for the boxplotax_bottom.set(xlabel='')ax_right.set(ylabel='')# Main Title, Xlabel and YLabelax_main.set(title='Scatterplot with Histograms \\n displ vs hwy', xlabel='displ', ylabel='hwy')# Set font size of different componentsax_main.title.set_fontsize(20)for item in ([ax_main.xaxis.label, ax_main.yaxis.label] + ax_main.get_xticklabels() + ax_main.get_yticklabels()): item.set_fontsize(14)plt.show() 【08】相关图(Correllogram)相关图用于直观地查看给定数据帧(或二维数组)中所有可能的数值变量对之间的相关性度量。 123456789101112# Import Datasetdf = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mtcars.csv\")# Plotplt.figure(figsize=(12, 10), dpi=80)sns.heatmap(df.corr(), xticklabels=df.corr().columns, yticklabels=df.corr().columns, cmap='RdYlGn', center=0, annot=True)# Decorationsplt.title('Correlogram of mtcars', fontsize=22)plt.xticks(fontsize=12)plt.yticks(fontsize=12)plt.show() 【09】成对图(Pairwise Plot)成对图是探索性分析中最受欢迎的一种方法,用来理解所有可能的数值变量对之间的关系。它是二元分析的必备工具。 1234567# Load Datasetdf = sns.load_dataset('iris')# Plotplt.figure(figsize=(10, 8), dpi=80)sns.pairplot(df, kind=\"scatter\", hue=\"species\", plot_kws=dict(s=80, edgecolor=\"white\", linewidth=2.5))plt.show() 1234567# Load Datasetdf = sns.load_dataset('iris')# Plotplt.figure(figsize=(10, 8), dpi=80)sns.pairplot(df, kind=\"reg\", hue=\"species\")plt.show() 【4x00】偏差(Deviation)【10】发散型条形图(Diverging Bars)如果您想根据单个指标查看项目的变化情况,并可视化此差异的顺序和数量,那么散型条形图是一个很好的工具。 它有助于快速区分数据组的性能,并且非常直观,并且可以立即传达这一点。 123456789101112131415161718# Prepare Datadf = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mtcars.csv\")x = df.loc[:, ['mpg']]df['mpg_z'] = (x - x.mean())/x.std()df['colors'] = ['red' if x < 0 else 'green' for x in df['mpg_z']]df.sort_values('mpg_z', inplace=True)df.reset_index(inplace=True)# Draw plotplt.figure(figsize=(14,10), dpi= 80)plt.hlines(y=df.index, xmin=0, xmax=df.mpg_z, color=df.colors, alpha=0.4, linewidth=5)# Decorationsplt.gca().set(ylabel='$Model$', xlabel='$Mileage$')plt.yticks(df.index, df.cars, fontsize=12)plt.title('Diverging Bars of Car Mileage', fontdict={'size':20})plt.grid(linestyle='--', alpha=0.5)plt.show() 【11】发散型文本图(Diverging Texts)发散型文本图与发散型条形图相似,如果你希望以一种美观的方式显示图表中每个项目的值,就可以使用这种方法。 123456789101112131415161718192021# Prepare Datadf = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mtcars.csv\")x = df.loc[:, ['mpg']]df['mpg_z'] = (x - x.mean())/x.std()df['colors'] = ['red' if x < 0 else 'green' for x in df['mpg_z']]df.sort_values('mpg_z', inplace=True)df.reset_index(inplace=True)# Draw plotplt.figure(figsize=(14, 14), dpi=80)plt.hlines(y=df.index, xmin=0, xmax=df.mpg_z)for x, y, tex in zip(df.mpg_z, df.index, df.mpg_z): t = plt.text(x, y, round(tex, 2), horizontalalignment='right' if x < 0 else 'left', verticalalignment='center', fontdict={'color':'red' if x < 0 else 'green', 'size':14})# Decorationsplt.yticks(df.index, df.cars, fontsize=12)plt.title('Diverging Text Bars of Car Mileage', fontdict={'size':20})plt.grid(linestyle='--', alpha=0.5)plt.xlim(-2.5, 2.5)plt.show() 【12】发散型散点图(Diverging Dot Plot)发散型散点图类似于发散型条形图。 但是,与发散型条形图相比,没有条形会减少组之间的对比度和差异。 12345678910111213141516171819202122232425262728# Prepare Datadf = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mtcars.csv\")x = df.loc[:, ['mpg']]df['mpg_z'] = (x - x.mean())/x.std()df['colors'] = ['red' if x < 0 else 'darkgreen' for x in df['mpg_z']]df.sort_values('mpg_z', inplace=True)df.reset_index(inplace=True)# Draw plotplt.figure(figsize=(14, 16), dpi=80)plt.scatter(df.mpg_z, df.index, s=450, alpha=.6, color=df.colors)for x, y, tex in zip(df.mpg_z, df.index, df.mpg_z): t = plt.text(x, y, round(tex, 1), horizontalalignment='center', verticalalignment='center', fontdict={'color': 'white'})# Decorations# Lighten bordersplt.gca().spines[\"top\"].set_alpha(.3)plt.gca().spines[\"bottom\"].set_alpha(.3)plt.gca().spines[\"right\"].set_alpha(.3)plt.gca().spines[\"left\"].set_alpha(.3)plt.yticks(df.index, df.cars)plt.title('Diverging Dotplot of Car Mileage', fontdict={'size': 20})plt.xlabel('$Mileage$')plt.grid(linestyle='--', alpha=0.5)plt.xlim(-2.5, 2.5)plt.show() 【13】带标记的发散型棒棒糖图(Diverging Lollipop Chart with Markers)带有标记的棒棒糖提供了一种灵活的方式,强调您想要引起注意的任何重要数据点并在图表中适当地给出推理。 12345678910111213141516171819202122232425262728293031323334353637# Prepare Datadf = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mtcars.csv\")x = df.loc[:, ['mpg']]df['mpg_z'] = (x - x.mean())/x.std()df['colors'] = 'black'# color fiat differentlydf.loc[df.cars == 'Fiat X1-9', 'colors'] = 'darkorange'df.sort_values('mpg_z', inplace=True)df.reset_index(inplace=True)# Draw plotimport matplotlib.patches as patchesplt.figure(figsize=(14, 16), dpi=80)plt.hlines(y=df.index, xmin=0, xmax=df.mpg_z, color=df.colors, alpha=0.4, linewidth=1)plt.scatter(df.mpg_z, df.index, color=df.colors, s=[600 if x == 'Fiat X1-9' else 300 for x in df.cars], alpha=0.6)plt.yticks(df.index, df.cars)plt.xticks(fontsize=12)# Annotateplt.annotate('Mercedes Models', xy=(0.0, 11.0), xytext=(1.0, 11), xycoords='data', fontsize=15, ha='center', va='center', bbox=dict(boxstyle='square', fc='firebrick'), arrowprops=dict(arrowstyle='-[, widthB=2.0, lengthB=1.5', lw=2.0, color='steelblue'), color='white')# Add Patchesp1 = patches.Rectangle((-2.0, -1), width=.3, height=3, alpha=.2, facecolor='red')p2 = patches.Rectangle((1.5, 27), width=.8, height=5, alpha=.2, facecolor='green')plt.gca().add_patch(p1)plt.gca().add_patch(p2)# Decorateplt.title('Diverging Bars of Car Mileage', fontdict={'size': 20})plt.grid(linestyle='--', alpha=0.5)plt.show() 【14】面积图(Area Chart)通过对轴和线之间的区域进行着色,面积图不仅强调波峰和波谷,还强调波峰和波谷的持续时间。 高点持续时间越长,线下面积越大。 1234567891011121314151617181920212223242526272829import numpy as npimport pandas as pd# Prepare Datadf = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/economics.csv\", parse_dates=['date']).head(100)x = np.arange(df.shape[0])y_returns = (df.psavert.diff().fillna(0)/df.psavert.shift(1)).fillna(0) * 100# Plotplt.figure(figsize=(16, 10), dpi=80)plt.fill_between(x[1:], y_returns[1:], 0, where=y_returns[1:] >= 0, facecolor='green', interpolate=True, alpha=0.7)plt.fill_between(x[1:], y_returns[1:], 0, where=y_returns[1:] <= 0, facecolor='red', interpolate=True, alpha=0.7)# Annotateplt.annotate('Peak \\n1975', xy=(94.0, 21.0), xytext=(88.0, 28), bbox=dict(boxstyle='square', fc='firebrick'), arrowprops=dict(facecolor='steelblue', shrink=0.05), fontsize=15, color='white')# Decorationsxtickvals = [str(m)[:3].upper()+\"-\"+str(y) for y, m in zip(df.date.dt.year, df.date.dt.month_name())]plt.gca().set_xticks(x[::6])plt.gca().set_xticklabels(xtickvals[::6], rotation=90, fontdict={'horizontalalignment': 'center', 'verticalalignment': 'center_baseline'})plt.ylim(-35, 35)plt.xlim(1, 100)plt.title(\"Month Economics Return %\", fontsize=22)plt.ylabel('Monthly returns %')plt.grid(alpha=0.5)plt.show() 【5x00】排序(Ranking)【15】有序条形图(Ordered Bar Chart)有序条形图有效地传达了项目的排序顺序。在图表上方添加度量标准的值,用户就可以从图表本身获得精确的信息。 12345678910111213141516171819202122232425262728# Prepare Datadf_raw = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mpg_ggplot2.csv\")df = df_raw[['cty', 'manufacturer']].groupby('manufacturer').apply(lambda x: x.mean())df.sort_values('cty', inplace=True)df.reset_index(inplace=True)# Draw plotimport matplotlib.patches as patchesfig, ax = plt.subplots(figsize=(16,10), facecolor='white', dpi= 80)ax.vlines(x=df.index, ymin=0, ymax=df.cty, color='firebrick', alpha=0.7, linewidth=20)# Annotate Textfor i, cty in enumerate(df.cty): ax.text(i, cty+0.5, round(cty, 1), horizontalalignment='center')# Title, Label, Ticks and Ylimax.set_title('Bar Chart for Highway Mileage', fontdict={'size':22})ax.set(ylabel='Miles Per Gallon', ylim=(0, 30))plt.xticks(df.index, df.manufacturer.str.upper(), rotation=60, horizontalalignment='right', fontsize=12)# Add patches to color the X axis labelsp1 = patches.Rectangle((.57, -0.005), width=.33, height=.13, alpha=.1, facecolor='green', transform=fig.transFigure)p2 = patches.Rectangle((.124, -0.005), width=.446, height=.13, alpha=.1, facecolor='red', transform=fig.transFigure)fig.add_artist(p1)fig.add_artist(p2)plt.show() 【16】棒棒糖图(Lollipop Chart)棒棒糖图表以一种视觉上令人愉悦的方式提供与有序条形图类似的目的。 1234567891011121314151617181920212223# Prepare Datadf_raw = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mpg_ggplot2.csv\")df = df_raw[['cty', 'manufacturer']].groupby('manufacturer').apply(lambda x: x.mean())df.sort_values('cty', inplace=True)df.reset_index(inplace=True)# Draw plotfig, ax = plt.subplots(figsize=(16, 10), dpi=80)ax.vlines(x=df.index, ymin=0, ymax=df.cty, color='firebrick', alpha=0.7, linewidth=2)ax.scatter(x=df.index, y=df.cty, s=75, color='firebrick', alpha=0.7)# Title, Label, Ticks and Ylimax.set_title('Lollipop Chart for Highway Mileage', fontdict={'size': 22})ax.set_ylabel('Miles Per Gallon')ax.set_xticks(df.index)ax.set_xticklabels(df.manufacturer.str.upper(), rotation=60, fontdict={'horizontalalignment': 'right', 'size': 12})ax.set_ylim(0, 30)# Annotatefor row in df.itertuples(): ax.text(row.Index, row.cty+.5, s=round(row.cty, 2), horizontalalignment='center', verticalalignment='bottom', fontsize=14)plt.show() 【17】点图(Dot Plot)点图可以表示项目的排名顺序。由于它是沿水平轴对齐的,所以可以更容易地看到点之间的距离。 123456789101112131415161718# Prepare Datadf_raw = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mpg_ggplot2.csv\")df = df_raw[['cty', 'manufacturer']].groupby('manufacturer').apply(lambda x: x.mean())df.sort_values('cty', inplace=True)df.reset_index(inplace=True)# Draw plotfig, ax = plt.subplots(figsize=(16, 10), dpi=80)ax.hlines(y=df.index, xmin=11, xmax=26, color='gray', alpha=0.7, linewidth=1, linestyles='dashdot')ax.scatter(y=df.index, x=df.cty, s=75, color='firebrick', alpha=0.7)# Title, Label, Ticks and Ylimax.set_title('Dot Plot for Highway Mileage', fontdict={'size': 22})ax.set_xlabel('Miles Per Gallon')ax.set_yticks(df.index)ax.set_yticklabels(df.manufacturer.str.title(), fontdict={'horizontalalignment': 'right'})ax.set_xlim(10, 27)plt.show() 【18】坡度图(Slope Chart)坡度图最适合比较给定人员/项目的“前”和“后”位置。 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657import matplotlib.lines as mlines# Import Datadf = pd.read_csv(\"https://raw.githubusercontent.com/selva86/datasets/master/gdppercap.csv\")left_label = [str(c) + ', ' + str(round(y)) for c, y in zip(df.continent, df['1952'])]right_label = [str(c) + ', ' + str(round(y)) for c, y in zip(df.continent, df['1957'])]klass = ['red' if (y1 - y2) < 0 else 'green' for y1, y2 in zip(df['1952'], df['1957'])]# draw line# https://stackoverflow.com/questions/36470343/how-to-draw-a-line-with-matplotlib/36479941def newline(p1, p2, color='black'): ax = plt.gca() l = mlines.Line2D([p1[0], p2[0]], [p1[1], p2[1]], color='red' if p1[1] - p2[1] > 0 else 'green', marker='o', markersize=6) ax.add_line(l) return lfig, ax = plt.subplots(1, 1, figsize=(14, 14), dpi=80)# Vertical Linesax.vlines(x=1, ymin=500, ymax=13000, color='black', alpha=0.7, linewidth=1, linestyles='dotted')ax.vlines(x=3, ymin=500, ymax=13000, color='black', alpha=0.7, linewidth=1, linestyles='dotted')# Pointsax.scatter(y=df['1952'], x=np.repeat(1, df.shape[0]), s=10, color='black', alpha=0.7)ax.scatter(y=df['1957'], x=np.repeat(3, df.shape[0]), s=10, color='black', alpha=0.7)# Line Segmentsand Annotationfor p1, p2, c in zip(df['1952'], df['1957'], df['continent']): newline([1, p1], [3, p2]) ax.text(1 - 0.05, p1, c + ', ' + str(round(p1)), horizontalalignment='right', verticalalignment='center', fontdict={'size': 14}) ax.text(3 + 0.05, p2, c + ', ' + str(round(p2)), horizontalalignment='left', verticalalignment='center', fontdict={'size': 14})# 'Before' and 'After' Annotationsax.text(1 - 0.05, 13000, 'BEFORE', horizontalalignment='right', verticalalignment='center', fontdict={'size': 18, 'weight': 700})ax.text(3 + 0.05, 13000, 'AFTER', horizontalalignment='left', verticalalignment='center', fontdict={'size': 18, 'weight': 700})# Decorationax.set_title(\"Slopechart: Comparing GDP Per Capita between 1952 vs 1957\", fontdict={'size': 22})ax.set(xlim=(0, 4), ylim=(0, 14000), ylabel='Mean GDP Per Capita')ax.set_xticks([1, 3])ax.set_xticklabels([\"1952\", \"1957\"])plt.yticks(np.arange(500, 13000, 2000), fontsize=12)# Lighten bordersplt.gca().spines[\"top\"].set_alpha(.0)plt.gca().spines[\"bottom\"].set_alpha(.0)plt.gca().spines[\"right\"].set_alpha(.0)plt.gca().spines[\"left\"].set_alpha(.0)plt.show() 【19】哑铃图(Dumbbell Plot)哑铃图传达了各种项目的“前”和“后”位置以及项目的等级顺序。如果您希望可视化特定项目/计划对不同对象的影响,那么它非常有用。 1234567891011121314151617181920212223242526272829303132333435363738394041import matplotlib.lines as mlines# Import Datadf = pd.read_csv(\"https://raw.githubusercontent.com/selva86/datasets/master/health.csv\")df.sort_values('pct_2014', inplace=True)df.reset_index(inplace=True)# Func to draw line segmentdef newline(p1, p2, color='black'): ax = plt.gca() l = mlines.Line2D([p1[0], p2[0]], [p1[1], p2[1]], color='skyblue') ax.add_line(l) return l# Figure and Axesfig, ax = plt.subplots(1, 1, figsize=(14, 14), facecolor='#f7f7f7', dpi=80)# Vertical Linesax.vlines(x=.05, ymin=0, ymax=26, color='black', alpha=1, linewidth=1, linestyles='dotted')ax.vlines(x=.10, ymin=0, ymax=26, color='black', alpha=1, linewidth=1, linestyles='dotted')ax.vlines(x=.15, ymin=0, ymax=26, color='black', alpha=1, linewidth=1, linestyles='dotted')ax.vlines(x=.20, ymin=0, ymax=26, color='black', alpha=1, linewidth=1, linestyles='dotted')# Pointsax.scatter(y=df['index'], x=df['pct_2013'], s=50, color='#0e668b', alpha=0.7)ax.scatter(y=df['index'], x=df['pct_2014'], s=50, color='#a3c4dc', alpha=0.7)# Line Segmentsfor i, p1, p2 in zip(df['index'], df['pct_2013'], df['pct_2014']): newline([p1, i], [p2, i])# Decorationax.set_facecolor('#f7f7f7')ax.set_title(\"Dumbell Chart: Pct Change - 2013 vs 2014\", fontdict={'size': 22})ax.set(xlim=(0, .25), ylim=(-1, 27), ylabel='Mean GDP Per Capita')ax.set_xticks([.05, .1, .15, .20])ax.set_xticklabels(['5%', '15%', '20%', '25%'])ax.set_xticklabels(['5%', '15%', '20%', '25%'])plt.show() 【6x00】分布(Distribution)【20】连续变量的直方图(Histogram for Continuous Variable)连续变量的直方图显示给定变量的频率分布。下面的图表基于分类变量对频率条进行分组,从而更深入地了解连续变量和分类变量。 12345678910111213141516171819202122# Import Datadf = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mpg_ggplot2.csv\")# Prepare datax_var = 'displ'groupby_var = 'class'df_agg = df.loc[:, [x_var, groupby_var]].groupby(groupby_var)vals = [df[x_var].values.tolist() for i, df in df_agg]# Drawplt.figure(figsize=(16, 9), dpi=80)colors = [plt.cm.Spectral(i / float(len(vals) - 1)) for i in range(len(vals))]n, bins, patches = plt.hist(vals, 30, stacked=True, density=False, color=colors[:len(vals)])# Decorationplt.legend({group: col for group, col in zip(np.unique(df[groupby_var]).tolist(), colors[:len(vals)])})plt.title(f\"Stacked Histogram of ${x_var}$ colored by ${groupby_var}$\", fontsize=22)plt.xlabel(x_var)plt.ylabel(\"Frequency\")plt.ylim(0, 25)plt.xticks(ticks=bins[::3], labels=[round(b, 1) for b in bins[::3]])plt.show() 【21】分类变量的直方图(Histogram for Categorical Variable)分类变量的直方图显示该变量的频率分布。通过给条形图上色,您可以将分布与表示颜色的另一个类型变量相关联。 12345678910111213141516171819202122# Import Datadf = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mpg_ggplot2.csv\")# Prepare datax_var = 'manufacturer'groupby_var = 'class'df_agg = df.loc[:, [x_var, groupby_var]].groupby(groupby_var)vals = [df[x_var].values.tolist() for i, df in df_agg]# Drawplt.figure(figsize=(16, 9), dpi=80)colors = [plt.cm.Spectral(i / float(len(vals) - 1)) for i in range(len(vals))]n, bins, patches = plt.hist(vals, df[x_var].unique().__len__(), stacked=True, density=False, color=colors[:len(vals)])# Decorationplt.legend({group: col for group, col in zip(np.unique(df[groupby_var]).tolist(), colors[:len(vals)])})plt.title(f\"Stacked Histogram of ${x_var}$ colored by ${groupby_var}$\", fontsize=22)plt.xlabel(x_var)plt.ylabel(\"Frequency\")plt.ylim(0, 40)plt.xticks(ticks=bins, labels=np.unique(df[x_var]).tolist(), rotation=90, horizontalalignment='left')plt.show() 【22】密度图(Density Plot)密度图是连续变量分布可视化的常用工具。通过按“response”变量对它们进行分组,您可以检查 X 和 Y 之间的关系。如果出于代表性目的来描述城市里程分布如何随气缸数而变化,请参见下面的情况。 1234567891011121314# Import Datadf = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mpg_ggplot2.csv\")# Draw Plotplt.figure(figsize=(16, 10), dpi=80)sns.kdeplot(df.loc[df['cyl'] == 4, \"cty\"], shade=True, color=\"g\", label=\"Cyl=4\", alpha=.7)sns.kdeplot(df.loc[df['cyl'] == 5, \"cty\"], shade=True, color=\"deeppink\", label=\"Cyl=5\", alpha=.7)sns.kdeplot(df.loc[df['cyl'] == 6, \"cty\"], shade=True, color=\"dodgerblue\", label=\"Cyl=6\", alpha=.7)sns.kdeplot(df.loc[df['cyl'] == 8, \"cty\"], shade=True, color=\"orange\", label=\"Cyl=8\", alpha=.7)# Decorationplt.title('Density Plot of City Mileage by n_Cylinders', fontsize=22)plt.legend()plt.show() 【23】直方图密度曲线(Density Curves with Histogram)具有直方图的密度曲线将两个图所传达的信息集合在一起,因此您可以将它们都放在一个图形中,而不是放在两个图形中。 1234567891011121314151617# Import Datadf = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mpg_ggplot2.csv\")# Draw Plotplt.figure(figsize=(13, 10), dpi=80)sns.distplot(df.loc[df['class'] == 'compact', \"cty\"], color=\"dodgerblue\", label=\"Compact\", hist_kws={'alpha': .7}, kde_kws={'linewidth': 3})sns.distplot(df.loc[df['class'] == 'suv', \"cty\"], color=\"orange\", label=\"SUV\", hist_kws={'alpha': .7}, kde_kws={'linewidth': 3})sns.distplot(df.loc[df['class'] == 'minivan', \"cty\"], color=\"g\", label=\"minivan\", hist_kws={'alpha': .7}, kde_kws={'linewidth': 3})plt.ylim(0, 0.35)# Decorationplt.title('Density Plot of City Mileage by Vehicle Type', fontsize=22)plt.legend()plt.show() 【24】山峰叠峦图 / 欢乐图(Joy Plot)Joy Plot 允许不同组的密度曲线重叠,这是一种很好的可视化方法,可以直观地显示大量分组之间的关系。它看起来赏心悦目,清楚地传达了正确的信息。它可以使用基于 matplotlib 的 joypy 包轻松构建。 【译者 TRHX 注:Joy Plot 看起来就像是山峰叠峦,山峦起伏,层次分明,但取名为 Joy,欢乐的意思,所以不太好翻译,在使用该方法时要先安装 joypy 库】 1234567891011121314# !pip install joypy# Import Dataimport joypy# 原文没有 import joypy,译者 TRHX 添加mpg = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mpg_ggplot2.csv\")# Draw Plotplt.figure(figsize=(16, 10), dpi=80)fig, axes = joypy.joyplot(mpg, column=['hwy', 'cty'], by=\"class\", ylim='own', figsize=(14, 10))# Decorationplt.title('Joy Plot of City and Highway Mileage by Class', fontsize=22)plt.show() 【25】分布式点图(Distributed Dot Plot)分布点图显示按组分割的点的单变量分布。点越暗,数据点在该区域的集中程度就越高。通过对中值进行不同的着色,这些组的真实位置立即变得明显。 1234567891011121314151617181920212223242526272829303132333435363738394041import matplotlib.patches as mpatches# Prepare Datadf_raw = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mpg_ggplot2.csv\")cyl_colors = {4: 'tab:red', 5: 'tab:green', 6: 'tab:blue', 8: 'tab:orange'}df_raw['cyl_color'] = df_raw.cyl.map(cyl_colors)# Mean and Median city mileage by makedf = df_raw[['cty', 'manufacturer']].groupby('manufacturer').apply(lambda x: x.mean())df.sort_values('cty', ascending=False, inplace=True)df.reset_index(inplace=True)df_median = df_raw[['cty', 'manufacturer']].groupby('manufacturer').apply(lambda x: x.median())# Draw horizontal linesfig, ax = plt.subplots(figsize=(16, 10), dpi=80)ax.hlines(y=df.index, xmin=0, xmax=40, color='gray', alpha=0.5, linewidth=.5, linestyles='dashdot')# Draw the Dotsfor i, make in enumerate(df.manufacturer): df_make = df_raw.loc[df_raw.manufacturer == make, :] ax.scatter(y=np.repeat(i, df_make.shape[0]), x='cty', data=df_make, s=75, edgecolors='gray', c='w', alpha=0.5) ax.scatter(y=i, x='cty', data=df_median.loc[df_median.index == make, :], s=75, c='firebrick')# Annotateax.text(33, 13, \"$red \\; dots \\; are \\; the \\: median$\", fontdict={'size': 12}, color='firebrick')# Decorationsred_patch = plt.plot([], [], marker=\"o\", ms=10, ls=\"\", mec=None, color='firebrick', label=\"Median\")plt.legend(handles=red_patch)ax.set_title('Distribution of City Mileage by Make', fontdict={'size': 22})ax.set_xlabel('Miles Per Gallon (City)', alpha=0.7)ax.set_yticks(df.index)ax.set_yticklabels(df.manufacturer.str.title(), fontdict={'horizontalalignment': 'right'}, alpha=0.7)ax.set_xlim(1, 40)plt.xticks(alpha=0.7)plt.gca().spines[\"top\"].set_visible(False)plt.gca().spines[\"bottom\"].set_visible(False)plt.gca().spines[\"right\"].set_visible(False)plt.gca().spines[\"left\"].set_visible(False)plt.grid(axis='both', alpha=.4, linewidth=.1)plt.show() 【26】箱形图(Box Plot)箱形图是可视化分布的一种好方法,同时牢记中位数,第 25 个第 75 个四分位数和离群值。 但是,在解释方框的大小时需要小心,这可能会扭曲该组中包含的点数。 因此,手动提供每个框中的观察次数可以帮助克服此缺点。 例如,左侧的前两个框,尽管它们分别具有 5 和 47 个 obs,但是却具有相同大小, 因此,有必要写下该组中的观察数。 123456789101112131415161718192021222324# Import Datadf = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mpg_ggplot2.csv\")# Draw Plotplt.figure(figsize=(13, 10), dpi=80)sns.boxplot(x='class', y='hwy', data=df, notch=False)# Add N Obs inside boxplot (optional)def add_n_obs(df, group_col, y): medians_dict = {grp[0]: grp[1][y].median() for grp in df.groupby(group_col)} xticklabels = [x.get_text() for x in plt.gca().get_xticklabels()] n_obs = df.groupby(group_col)[y].size().values for (x, xticklabel), n_ob in zip(enumerate(xticklabels), n_obs): plt.text(x, medians_dict[xticklabel] * 1.01, \"#obs : \" + str(n_ob), horizontalalignment='center', fontdict={'size': 14}, color='white')add_n_obs(df, group_col='class', y='hwy')# Decorationplt.title('Box Plot of Highway Mileage by Vehicle Class', fontsize=22)plt.ylim(10, 40)plt.show() 【27】点 + 箱形图(Dot + Box Plot)点 + 箱形图传达类似于分组的箱形图信息。此外,这些点还提供了每组中有多少数据点的含义。 123456789101112131415# Import Datadf = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mpg_ggplot2.csv\")# Draw Plotplt.figure(figsize=(13,10), dpi= 80)sns.boxplot(x='class', y='hwy', data=df, hue='cyl')sns.stripplot(x='class', y='hwy', data=df, color='black', size=3, jitter=1)for i in range(len(df['class'].unique())-1): plt.vlines(i+.5, 10, 45, linestyles='solid', colors='gray', alpha=0.2)# Decorationplt.title('Box Plot of Highway Mileage by Vehicle Class', fontsize=22)plt.legend(title='Cylinders')plt.show() 【28】小提琴图(Violin Plot)小提琴图是箱形图在视觉上令人愉悦的替代品。 小提琴的形状或面积取决于它所持有的观察次数。 但是,小提琴图可能更难以阅读,并且在专业设置中不常用。 12345678910# Import Datadf = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mpg_ggplot2.csv\")# Draw Plotplt.figure(figsize=(13, 10), dpi=80)sns.violinplot(x='class', y='hwy', data=df, scale='width', inner='quartile')# Decorationplt.title('Violin Plot of Highway Mileage by Vehicle Class', fontsize=22)plt.show() 【29】人口金字塔图(Population Pyramid)人口金字塔可用于显示按体积排序的组的分布。或者它也可以用于显示人口的逐级过滤,因为它是用来显示有多少人通过一个营销漏斗(Marketing Funnel)的每个阶段。 12345678910111213141516171819# Read datadf = pd.read_csv(\"https://raw.githubusercontent.com/selva86/datasets/master/email_campaign_funnel.csv\")# Draw Plotplt.figure(figsize=(13, 10), dpi=80)group_col = 'Gender'order_of_bars = df.Stage.unique()[::-1]colors = [plt.cm.Spectral(i / float(len(df[group_col].unique()) - 1)) for i in range(len(df[group_col].unique()))]for c, group in zip(colors, df[group_col].unique()): sns.barplot(x='Users', y='Stage', data=df.loc[df[group_col] == group, :], order=order_of_bars, color=c, label=group)# Decorationsplt.xlabel(\"$Users$\")plt.ylabel(\"Stage of Purchase\")plt.yticks(fontsize=12)plt.title(\"Population Pyramid of the Marketing Funnel\", fontsize=22)plt.legend()plt.show() 【30】分类图(Categorical Plots)由 seaborn 库提供的分类图可用于可视化彼此相关的两个或更多分类变量的计数分布。 123456789101112# Load Datasettitanic = sns.load_dataset(\"titanic\")# Plotg = sns.catplot(\"alive\", col=\"deck\", col_wrap=4, data=titanic[titanic.deck.notnull()], kind=\"count\", height=3.5, aspect=.8, palette='tab20')# 译者 TRHX 注释掉了这一行代码# fig.suptitle('sf')plt.show() 123456789101112# Load Datasettitanic = sns.load_dataset(\"titanic\")# Plotsns.catplot(x=\"age\", y=\"embark_town\", hue=\"sex\", col=\"class\", data=titanic[titanic.embark_town.notnull()], orient=\"h\", height=5, aspect=1, palette=\"tab10\", kind=\"violin\", dodge=True, cut=0, bw=.2)# 译者 TRHX 添加了这行代码plt.show() 【7x00】组成(Composition)【31】华夫饼图(Waffle Chart)华夫饼图可以使用 pywaffle 包创建,用于显示较大群体中的组的组成。 【译者 TRHX 注:在使用该方法时要先安装 pywaffle 库】 123456789101112131415161718192021222324252627282930# ! pip install pywaffle# Reference: https://stackoverflow.com/questions/41400136/how-to-do-waffle-charts-in-python-square-piechartfrom pywaffle import Waffle# Importdf_raw = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mpg_ggplot2.csv\")# Prepare Datadf = df_raw.groupby('class').size().reset_index(name='counts')n_categories = df.shape[0]colors = [plt.cm.inferno_r(i / float(n_categories)) for i in range(n_categories)]# Draw Plot and Decoratefig = plt.figure( FigureClass=Waffle, plots={ '111': { 'values': df['counts'], 'labels': [\"{0} ({1})\".format(n[0], n[1]) for n in df[['class', 'counts']].itertuples()], 'legend': {'loc': 'upper left', 'bbox_to_anchor': (1.05, 1), 'fontsize': 12}, 'title': {'label': '# Vehicles by Class', 'loc': 'center', 'fontsize': 18} }, }, rows=7, colors=colors, figsize=(16, 9))# 译者 TRHX 添加了这行代码plt.show() 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455# ! pip install pywafflefrom pywaffle import Waffle# Import# 译者 TRHX 取消注释了这行代码df_raw = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mpg_ggplot2.csv\")# Prepare Data# By Class Datadf_class = df_raw.groupby('class').size().reset_index(name='counts_class')n_categories = df_class.shape[0]colors_class = [plt.cm.Set3(i / float(n_categories)) for i in range(n_categories)]# By Cylinders Datadf_cyl = df_raw.groupby('cyl').size().reset_index(name='counts_cyl')n_categories = df_cyl.shape[0]colors_cyl = [plt.cm.Spectral(i / float(n_categories)) for i in range(n_categories)]# By Make Datadf_make = df_raw.groupby('manufacturer').size().reset_index(name='counts_make')n_categories = df_make.shape[0]colors_make = [plt.cm.tab20b(i / float(n_categories)) for i in range(n_categories)]# Draw Plot and Decoratefig = plt.figure( FigureClass=Waffle, plots={ '311': { 'values': df_class['counts_class'], 'labels': [\"{1}\".format(n[0], n[1]) for n in df_class[['class', 'counts_class']].itertuples()], 'legend': {'loc': 'upper left', 'bbox_to_anchor': (1.05, 1), 'fontsize': 12, 'title': 'Class'}, 'title': {'label': '# Vehicles by Class', 'loc': 'center', 'fontsize': 18}, 'colors': colors_class }, '312': { 'values': df_cyl['counts_cyl'], 'labels': [\"{1}\".format(n[0], n[1]) for n in df_cyl[['cyl', 'counts_cyl']].itertuples()], 'legend': {'loc': 'upper left', 'bbox_to_anchor': (1.05, 1), 'fontsize': 12, 'title': 'Cyl'}, 'title': {'label': '# Vehicles by Cyl', 'loc': 'center', 'fontsize': 18}, 'colors': colors_cyl }, '313': { 'values': df_make['counts_make'], 'labels': [\"{1}\".format(n[0], n[1]) for n in df_make[['manufacturer', 'counts_make']].itertuples()], 'legend': {'loc': 'upper left', 'bbox_to_anchor': (1.05, 1), 'fontsize': 12, 'title': 'Manufacturer'}, 'title': {'label': '# Vehicles by Make', 'loc': 'center', 'fontsize': 18}, 'colors': colors_make } }, rows=9, figsize=(16, 14))# 译者 TRHX 添加了这行代码plt.show() 【32】饼图(Pie Chart)饼图是显示组成的经典方法。然而,现在一般不宜使用,因为馅饼部分的面积有时会产生误导。因此,如果要使用饼图,强烈建议您显式地记下饼图每个部分的百分比或数字。 123456789101112131415# Importdf_raw = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mpg_ggplot2.csv\")# Prepare Datadf = df_raw.groupby('class').size()# Make the plot with pandas'''原代码:df.plot(kind='pie', subplots=True, figsize=(8, 8), dpi=80)译者 TRHX 删除了 dpi= 80'''df.plot(kind='pie', subplots=True, figsize=(8, 8))plt.title(\"Pie Chart of Vehicle Class - Bad\")plt.ylabel(\"\")plt.show() 12345678910111213141516171819202122232425262728293031# Importdf_raw = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mpg_ggplot2.csv\")# Prepare Datadf = df_raw.groupby('class').size().reset_index(name='counts')# Draw Plotfig, ax = plt.subplots(figsize=(12, 7), subplot_kw=dict(aspect=\"equal\"), dpi=80)data = df['counts']categories = df['class']explode = [0, 0, 0, 0, 0, 0.1, 0]def func(pct, allvals): absolute = int(pct / 100. * np.sum(allvals)) return \"{:.1f}% ({:d} )\".format(pct, absolute)wedges, texts, autotexts = ax.pie(data, autopct=lambda pct: func(pct, data), textprops=dict(color=\"w\"), colors=plt.cm.Dark2.colors, startangle=140, explode=explode)# Decorationax.legend(wedges, categories, title=\"Vehicle Class\", loc=\"center left\", bbox_to_anchor=(1, 0, 0.5, 1))plt.setp(autotexts, size=10, weight=700)ax.set_title(\"Class of Vehicles: Pie Chart\")plt.show() 【33】矩阵树形图(Treemap)矩阵树形图类似于饼图,它可以更好地完成工作而不会误导每个组的贡献。 【译者 TRHX 注:在使用该方法时要先安装 squarify 库】 1234567891011121314151617181920# pip install squarifyimport squarify# Import Datadf_raw = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mpg_ggplot2.csv\")# Prepare Datadf = df_raw.groupby('class').size().reset_index(name='counts')labels = df.apply(lambda x: str(x[0]) + \"\\n (\" + str(x[1]) + \")\", axis=1)sizes = df['counts'].values.tolist()colors = [plt.cm.Spectral(i / float(len(labels))) for i in range(len(labels))]# Draw Plotplt.figure(figsize=(12, 8), dpi=80)squarify.plot(sizes=sizes, label=labels, color=colors, alpha=.8)# Decorateplt.title('Treemap of Vechile Class')plt.axis('off')plt.show() 【34】条形图(Bar Chart)条形图是一种基于计数或任何给定度量的可视化项的经典方法。在下面的图表中,我为每个项目使用了不同的颜色,但您通常可能希望为所有项目选择一种颜色,除非您按组对它们进行着色。颜色名称存储在下面代码中的 all_colors 中。您可以通过在 plt.plot() 中设置 color 参数来更改条形的颜色。 123456789101112131415161718192021222324import random# Import Datadf_raw = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mpg_ggplot2.csv\")# Prepare Datadf = df_raw.groupby('manufacturer').size().reset_index(name='counts')n = df['manufacturer'].unique().__len__()+1all_colors = list(plt.cm.colors.cnames.keys())random.seed(100)c = random.choices(all_colors, k=n)# Plot Barsplt.figure(figsize=(16,10), dpi= 80)plt.bar(df['manufacturer'], df['counts'], color=c, width=.5)for i, val in enumerate(df['counts'].values): plt.text(i, val, float(val), horizontalalignment='center', verticalalignment='bottom', fontdict={'fontweight':500, 'size':12})# Decorationplt.gca().set_xticklabels(df['manufacturer'], rotation=60, horizontalalignment= 'right')plt.title(\"Number of Vehicles by Manaufacturers\", fontsize=22)plt.ylabel('# Vehicles')plt.ylim(0, 45)plt.show() 【8x00】变化(Change)【35】时间序列图(Time Series Plot)时间序列图用于可视化给定指标随时间的变化。在这里你可以看到 1949 年到 1969 年间的航空客运量是如何变化的。 12345678910111213141516171819202122# Import Datadf = pd.read_csv('https://github.com/selva86/datasets/raw/master/AirPassengers.csv')# Draw Plotplt.figure(figsize=(16, 10), dpi=80)plt.plot('date', 'traffic', data=df, color='tab:red')# Decorationplt.ylim(50, 750)xtick_location = df.index.tolist()[::12]xtick_labels = [x[-4:] for x in df.date.tolist()[::12]]plt.xticks(ticks=xtick_location, labels=xtick_labels, rotation=0, fontsize=12, horizontalalignment='center', alpha=.7)plt.yticks(fontsize=12, alpha=.7)plt.title(\"Air Passengers Traffic (1949 - 1969)\", fontsize=22)plt.grid(axis='both', alpha=.3)# Remove bordersplt.gca().spines[\"top\"].set_alpha(0.0)plt.gca().spines[\"bottom\"].set_alpha(0.3)plt.gca().spines[\"right\"].set_alpha(0.0)plt.gca().spines[\"left\"].set_alpha(0.3)plt.show() 【36】带波峰和波谷注释的时间序列图(Time Series with Peaks and Troughs Annotated)下面的时间序列绘制了所有的波峰和波谷,并注释了所选特殊事件的发生。 1234567891011121314151617181920212223242526272829303132333435363738394041# Import Datadf = pd.read_csv('https://github.com/selva86/datasets/raw/master/AirPassengers.csv')# Get the Peaks and Troughsdata = df['traffic'].valuesdoublediff = np.diff(np.sign(np.diff(data)))peak_locations = np.where(doublediff == -2)[0] + 1doublediff2 = np.diff(np.sign(np.diff(-1 * data)))trough_locations = np.where(doublediff2 == -2)[0] + 1# Draw Plotplt.figure(figsize=(16, 10), dpi=80)plt.plot('date', 'traffic', data=df, color='tab:blue', label='Air Traffic')plt.scatter(df.date[peak_locations], df.traffic[peak_locations], marker=mpl.markers.CARETUPBASE, color='tab:green', s=100, label='Peaks')plt.scatter(df.date[trough_locations], df.traffic[trough_locations], marker=mpl.markers.CARETDOWNBASE, color='tab:red', s=100, label='Troughs')# Annotatefor t, p in zip(trough_locations[1::5], peak_locations[::3]): plt.text(df.date[p], df.traffic[p] + 15, df.date[p], horizontalalignment='center', color='darkgreen') plt.text(df.date[t], df.traffic[t] - 35, df.date[t], horizontalalignment='center', color='darkred')# Decorationplt.ylim(50, 750)xtick_location = df.index.tolist()[::6]xtick_labels = df.date.tolist()[::6]plt.xticks(ticks=xtick_location, labels=xtick_labels, rotation=90, fontsize=12, alpha=.7)plt.title(\"Peak and Troughs of Air Passengers Traffic (1949 - 1969)\", fontsize=22)plt.yticks(fontsize=12, alpha=.7)# Lighten bordersplt.gca().spines[\"top\"].set_alpha(.0)plt.gca().spines[\"bottom\"].set_alpha(.3)plt.gca().spines[\"right\"].set_alpha(.0)plt.gca().spines[\"left\"].set_alpha(.3)plt.legend(loc='upper left')plt.grid(axis='y', alpha=.3)plt.show() 【37】自相关 (ACF) 和部分自相关 (PACF) 图(Autocorrelation (ACF) and Partial Autocorrelation (PACF) Plot)自相关图(ACF图)显示了时间序列与其自身滞后的相关性。 每条垂直线(在自相关图上)表示系列与滞后 0 之间的滞后的相关性。图中的蓝色阴影区域是显著性级别。 那些位于蓝线之上的滞后是显著的滞后。 那么如何解释呢? 对于航空乘客来说,我们看到超过 14 个滞后已经越过蓝线,因此意义重大。这意味着,14 年前的航空客运量对今天的交通量产生了影响。 另一方面,部分自相关图(PACF)显示了任何给定滞后(时间序列)相对于当前序列的自相关,但消除了中间滞后的贡献。 123456789101112131415161718192021from statsmodels.graphics.tsaplots import plot_acf, plot_pacf# Import Datadf = pd.read_csv('https://github.com/selva86/datasets/raw/master/AirPassengers.csv')# Draw Plotfig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 6), dpi=80)plot_acf(df.traffic.tolist(), ax=ax1, lags=50)plot_pacf(df.traffic.tolist(), ax=ax2, lags=20)# Decorate# lighten the bordersax1.spines[\"top\"].set_alpha(.3); ax2.spines[\"top\"].set_alpha(.3)ax1.spines[\"bottom\"].set_alpha(.3); ax2.spines[\"bottom\"].set_alpha(.3)ax1.spines[\"right\"].set_alpha(.3); ax2.spines[\"right\"].set_alpha(.3)ax1.spines[\"left\"].set_alpha(.3); ax2.spines[\"left\"].set_alpha(.3)# font size of tick labelsax1.tick_params(axis='both', labelsize=12)ax2.tick_params(axis='both', labelsize=12)plt.show() 【38】交叉相关图(Cross Correlation plot)交叉相关图显示了两个时间序列相互之间的滞后。 12345678910111213141516171819202122232425262728import statsmodels.tsa.stattools as stattools# Import Datadf = pd.read_csv('https://github.com/selva86/datasets/raw/master/mortality.csv')x = df['mdeaths']y = df['fdeaths']# Compute Cross Correlationsccs = stattools.ccf(x, y)[:100]nlags = len(ccs)# Compute the Significance level# ref: https://stats.stackexchange.com/questions/3115/cross-correlation-significance-in-r/3128#3128conf_level = 2 / np.sqrt(nlags)# Draw Plotplt.figure(figsize=(12, 7), dpi=80)plt.hlines(0, xmin=0, xmax=100, color='gray') # 0 axisplt.hlines(conf_level, xmin=0, xmax=100, color='gray')plt.hlines(-conf_level, xmin=0, xmax=100, color='gray')plt.bar(x=np.arange(len(ccs)), height=ccs, width=.3)# Decorationplt.title('$Cross\\; Correlation\\; Plot:\\; mdeaths\\; vs\\; fdeaths$', fontsize=22)plt.xlim(0, len(ccs))plt.show() 【39】时间序列分解图(Time Series Decomposition Plot)时间序列分解图将时间序列分解为趋势、季节和残差分量。 123456789101112131415from statsmodels.tsa.seasonal import seasonal_decomposefrom dateutil.parser import parse# Import Datadf = pd.read_csv('https://github.com/selva86/datasets/raw/master/AirPassengers.csv')dates = pd.DatetimeIndex([parse(d).strftime('%Y-%m-01') for d in df['date']])df.set_index(dates, inplace=True)# Decomposeresult = seasonal_decompose(df['traffic'], model='multiplicative')# Plotplt.rcParams.update({'figure.figsize': (10, 10)})result.plot().suptitle('Time Series Decomposition of Air Passengers')plt.show() 【40】多重时间序列(Multiple Time Series)您可以在同一图表上绘制多个测量相同值的时间序列,如下所示。 12345678910111213141516171819202122232425262728293031323334353637# Import Datadf = pd.read_csv('https://github.com/selva86/datasets/raw/master/mortality.csv')# Define the upper limit, lower limit, interval of Y axis and colorsy_LL = 100y_UL = int(df.iloc[:, 1:].max().max() * 1.1)y_interval = 400mycolors = ['tab:red', 'tab:blue', 'tab:green', 'tab:orange']# Draw Plot and Annotatefig, ax = plt.subplots(1, 1, figsize=(16, 9), dpi=80)columns = df.columns[1:]for i, column in enumerate(columns): plt.plot(df.date.values, df[column].values, lw=1.5, color=mycolors[i]) plt.text(df.shape[0] + 1, df[column].values[-1], column, fontsize=14, color=mycolors[i])# Draw Tick linesfor y in range(y_LL, y_UL, y_interval): plt.hlines(y, xmin=0, xmax=71, colors='black', alpha=0.3, linestyles=\"--\", lw=0.5)# Decorationsplt.tick_params(axis=\"both\", which=\"both\", bottom=False, top=False, labelbottom=True, left=False, right=False, labelleft=True)# Lighten bordersplt.gca().spines[\"top\"].set_alpha(.3)plt.gca().spines[\"bottom\"].set_alpha(.3)plt.gca().spines[\"right\"].set_alpha(.3)plt.gca().spines[\"left\"].set_alpha(.3)plt.title('Number of Deaths from Lung Diseases in the UK (1974-1979)', fontsize=22)plt.yticks(range(y_LL, y_UL, y_interval), [str(y) for y in range(y_LL, y_UL, y_interval)], fontsize=12)plt.xticks(range(0, df.shape[0], 12), df.date.values[::12], horizontalalignment='left', fontsize=12)plt.ylim(y_LL, y_UL)plt.xlim(-2, 80)plt.show() 【41】使用次要的 Y 轴来绘制不同范围的图形(Plotting with different scales using secondary Y axis)如果要显示在同一时间点测量两个不同数量的两个时间序列,则可以在右侧的次要 Y 轴上再绘制第二个系列。 12345678910111213141516171819202122232425262728293031# Import Datadf = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/economics.csv\")x = df['date']y1 = df['psavert']y2 = df['unemploy']# Plot Line1 (Left Y Axis)fig, ax1 = plt.subplots(1, 1, figsize=(16, 9), dpi=80)ax1.plot(x, y1, color='tab:red')# Plot Line2 (Right Y Axis)ax2 = ax1.twinx() # instantiate a second axes that shares the same x-axisax2.plot(x, y2, color='tab:blue')# Decorations# ax1 (left Y axis)ax1.set_xlabel('Year', fontsize=20)ax1.tick_params(axis='x', rotation=0, labelsize=12)ax1.set_ylabel('Personal Savings Rate', color='tab:red', fontsize=20)ax1.tick_params(axis='y', rotation=0, labelcolor='tab:red')ax1.grid(alpha=.4)# ax2 (right Y axis)ax2.set_ylabel(\"# Unemployed (1000's)\", color='tab:blue', fontsize=20)ax2.tick_params(axis='y', labelcolor='tab:blue')ax2.set_xticks(np.arange(0, len(x), 60))ax2.set_xticklabels(x[::60], rotation=90, fontdict={'fontsize': 10})ax2.set_title(\"Personal Savings Rate vs Unemployed: Plotting in Secondary Y Axis\", fontsize=22)fig.tight_layout()plt.show() 【42】带误差带的时间序列(Time Series with Error Bands)如果您有一个时间序列数据集,其中每个时间点(日期/时间戳)有多个观测值,则可以构造具有误差带的时间序列。下面您可以看到一些基于一天中不同时间的订单的示例。还有一个关于45天内到达的订单数量的例子。 在这种方法中,订单数量的平均值用白线表示。并计算95%的置信区间,并围绕平均值绘制。 1234567891011121314151617181920212223242526272829303132from scipy.stats import sem# Import Datadf = pd.read_csv(\"https://raw.githubusercontent.com/selva86/datasets/master/user_orders_hourofday.csv\")df_mean = df.groupby('order_hour_of_day').quantity.mean()df_se = df.groupby('order_hour_of_day').quantity.apply(sem).mul(1.96)# Plotplt.figure(figsize=(16, 10), dpi=80)plt.ylabel(\"# Orders\", fontsize=16)x = df_mean.indexplt.plot(x, df_mean, color=\"white\", lw=2)plt.fill_between(x, df_mean - df_se, df_mean + df_se, color=\"#3F5D7D\")# Decorations# Lighten bordersplt.gca().spines[\"top\"].set_alpha(0)plt.gca().spines[\"bottom\"].set_alpha(1)plt.gca().spines[\"right\"].set_alpha(0)plt.gca().spines[\"left\"].set_alpha(1)plt.xticks(x[::2], [str(d) for d in x[::2]], fontsize=12)plt.title(\"User Orders by Hour of Day (95% confidence)\", fontsize=22)plt.xlabel(\"Hour of Day\")s, e = plt.gca().get_xlim()plt.xlim(s, e)# Draw Horizontal Tick linesfor y in range(8, 20, 2): plt.hlines(y, xmin=s, xmax=e, colors='black', alpha=0.5, linestyles=\"--\", lw=0.5)plt.show() 1234567891011121314151617181920212223242526272829303132333435363738\"Data Source: https://www.kaggle.com/olistbr/brazilian-ecommerce#olist_orders_dataset.csv\"from dateutil.parser import parsefrom scipy.stats import sem# Import Datadf_raw = pd.read_csv('https://raw.githubusercontent.com/selva86/datasets/master/orders_45d.csv', parse_dates=['purchase_time', 'purchase_date'])# Prepare Data: Daily Mean and SE Bandsdf_mean = df_raw.groupby('purchase_date').quantity.mean()df_se = df_raw.groupby('purchase_date').quantity.apply(sem).mul(1.96)# Plotplt.figure(figsize=(16, 10), dpi=80)plt.ylabel(\"# Daily Orders\", fontsize=16)x = [d.date().strftime('%Y-%m-%d') for d in df_mean.index]plt.plot(x, df_mean, color=\"white\", lw=2)plt.fill_between(x, df_mean - df_se, df_mean + df_se, color=\"#3F5D7D\")# Decorations# Lighten bordersplt.gca().spines[\"top\"].set_alpha(0)plt.gca().spines[\"bottom\"].set_alpha(1)plt.gca().spines[\"right\"].set_alpha(0)plt.gca().spines[\"left\"].set_alpha(1)plt.xticks(x[::6], [str(d) for d in x[::6]], fontsize=12)plt.title(\"Daily Order Quantity of Brazilian Retail with Error Bands (95% confidence)\", fontsize=20)# Axis limitss, e = plt.gca().get_xlim()plt.xlim(s, e - 2)plt.ylim(4, 10)# Draw Horizontal Tick linesfor y in range(5, 10, 1): plt.hlines(y, xmin=s, xmax=e, colors='black', alpha=0.5, linestyles=\"--\", lw=0.5)plt.show() 【43】堆积面积图(Stacked Area Chart)堆积面积图提供了多个时间序列的贡献程度的可视化表示,以便相互比较。 12345678910111213141516171819202122232425262728293031323334353637383940414243# Import Datadf = pd.read_csv('https://raw.githubusercontent.com/selva86/datasets/master/nightvisitors.csv')# Decide Colorsmycolors = ['tab:red', 'tab:blue', 'tab:green', 'tab:orange', 'tab:brown', 'tab:grey', 'tab:pink', 'tab:olive']# Draw Plot and Annotatefig, ax = plt.subplots(1, 1, figsize=(16, 9), dpi=80)columns = df.columns[1:]labs = columns.values.tolist()# Prepare datax = df['yearmon'].values.tolist()y0 = df[columns[0]].values.tolist()y1 = df[columns[1]].values.tolist()y2 = df[columns[2]].values.tolist()y3 = df[columns[3]].values.tolist()y4 = df[columns[4]].values.tolist()y5 = df[columns[5]].values.tolist()y6 = df[columns[6]].values.tolist()y7 = df[columns[7]].values.tolist()y = np.vstack([y0, y2, y4, y6, y7, y5, y1, y3])# Plot for each columnlabs = columns.values.tolist()ax = plt.gca()ax.stackplot(x, y, labels=labs, colors=mycolors, alpha=0.8)# Decorationsax.set_title('Night Visitors in Australian Regions', fontsize=18)ax.set(ylim=[0, 100000])ax.legend(fontsize=10, ncol=4)plt.xticks(x[::5], fontsize=10, horizontalalignment='center')plt.yticks(np.arange(10000, 100000, 20000), fontsize=10)plt.xlim(x[0], x[-1])# Lighten bordersplt.gca().spines[\"top\"].set_alpha(0)plt.gca().spines[\"bottom\"].set_alpha(.3)plt.gca().spines[\"right\"].set_alpha(0)plt.gca().spines[\"left\"].set_alpha(.3)plt.show() 【44】未堆积面积图(Area Chart UnStacked)未堆积的面积图用于可视化两个或多个序列彼此之间的进度(起伏)。在下面的图表中,你可以清楚地看到,随着失业持续时间的中位数增加,个人储蓄率是如何下降的。未堆积面积图很好地展示了这一现象。 123456789101112131415161718192021222324252627282930313233# Import Datadf = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/economics.csv\")# Prepare Datax = df['date'].values.tolist()y1 = df['psavert'].values.tolist()y2 = df['uempmed'].values.tolist()mycolors = ['tab:red', 'tab:blue', 'tab:green', 'tab:orange', 'tab:brown', 'tab:grey', 'tab:pink', 'tab:olive']columns = ['psavert', 'uempmed']# Draw Plotfig, ax = plt.subplots(1, 1, figsize=(16, 9), dpi=80)ax.fill_between(x, y1=y1, y2=0, label=columns[1], alpha=0.5, color=mycolors[1], linewidth=2)ax.fill_between(x, y1=y2, y2=0, label=columns[0], alpha=0.5, color=mycolors[0], linewidth=2)# Decorationsax.set_title('Personal Savings Rate vs Median Duration of Unemployment', fontsize=18)ax.set(ylim=[0, 30])ax.legend(loc='best', fontsize=12)plt.xticks(x[::50], fontsize=10, horizontalalignment='center')plt.yticks(np.arange(2.5, 30.0, 2.5), fontsize=10)plt.xlim(-10, x[-1])# Draw Tick linesfor y in np.arange(2.5, 30.0, 2.5): plt.hlines(y, xmin=0, xmax=len(x), colors='black', alpha=0.3, linestyles=\"--\", lw=0.5)# Lighten bordersplt.gca().spines[\"top\"].set_alpha(0)plt.gca().spines[\"bottom\"].set_alpha(.3)plt.gca().spines[\"right\"].set_alpha(0)plt.gca().spines[\"left\"].set_alpha(.3)plt.show() 【45】日历热力图(Calendar Heat Map)与时间序列相比,日历地图是另一种基于时间的数据可视化的不太受欢迎的方法。虽然在视觉上很吸引人,但数值并不十分明显。然而,它能很好地描绘极端值和假日效果。 【译者 TRHX 注:在使用该方法时要先安装 calmap 库】 123456789101112import matplotlib as mplimport calmap# Import Datadf = pd.read_csv(\"https://raw.githubusercontent.com/selva86/datasets/master/yahoo.csv\", parse_dates=['date'])df.set_index('date', inplace=True)# Plotplt.figure(figsize=(16, 10), dpi=80)calmap.calendarplot(df['2014']['VIX.Close'], fig_kws={'figsize': (16, 10)}, yearlabel_kws={'color': 'black', 'fontsize': 14}, subplot_kws={'title': 'Yahoo Stock Prices'})plt.show() 【46】季节图(Seasonal Plot)季节图可用于比较上一季度同一天(年/月/周等)时间序列的表现。 1234567891011121314151617181920212223242526272829303132333435363738from dateutil.parser import parse# Import Datadf = pd.read_csv('https://github.com/selva86/datasets/raw/master/AirPassengers.csv')# Prepare datadf['year'] = [parse(d).year for d in df.date]df['month'] = [parse(d).strftime('%b') for d in df.date]years = df['year'].unique()# 译者 TRHX 添加了该行代码df.rename(columns={'value': 'traffic'}, inplace=True)# Draw Plotmycolors = ['tab:red', 'tab:blue', 'tab:green', 'tab:orange', 'tab:brown', 'tab:grey', 'tab:pink', 'tab:olive', 'deeppink', 'steelblue', 'firebrick', 'mediumseagreen']plt.figure(figsize=(16, 10), dpi=80)for i, y in enumerate(years): plt.plot('month', 'traffic', data=df.loc[df.year == y, :], color=mycolors[i], label=y) plt.text(df.loc[df.year == y, :].shape[0] - .9, df.loc[df.year == y, 'traffic'][-1:].values[0], y, fontsize=12, color=mycolors[i])# Decorationplt.ylim(50, 750)plt.xlim(-0.3, 11)plt.ylabel('$Air Traffic$')plt.yticks(fontsize=12, alpha=.7)plt.title(\"Monthly Seasonal Plot: Air Passengers Traffic (1949 - 1969)\", fontsize=22)plt.grid(axis='y', alpha=.3)# Remove bordersplt.gca().spines[\"top\"].set_alpha(0.0)plt.gca().spines[\"bottom\"].set_alpha(0.5)plt.gca().spines[\"right\"].set_alpha(0.0)plt.gca().spines[\"left\"].set_alpha(0.5)# plt.legend(loc='upper right', ncol=2, fontsize=12)plt.show() 【9x00】分组( Groups)【47】树状图(Dendrogram)树状图根据给定的距离度量将相似的点组合在一起,并根据点的相似性将它们组织成树状链接。 123456789101112import scipy.cluster.hierarchy as shc# Import Datadf = pd.read_csv('https://raw.githubusercontent.com/selva86/datasets/master/USArrests.csv')# Plotplt.figure(figsize=(16, 10), dpi=80)plt.title(\"USArrests Dendograms\", fontsize=22)dend = shc.dendrogram(shc.linkage(df[['Murder', 'Assault', 'UrbanPop', 'Rape']], method='ward'), labels=df.State.values, color_threshold=100)plt.xticks(fontsize=12)plt.show() 【48】聚类图(Cluster Plot)聚类图可以用来划分属于同一个聚类的点。下面是一个基于 USArrests 数据集将美国各州分成 5 组的代表性示例。这个聚类图使用 ‘murder’ 和 ‘assault’ 作为 X 轴和 Y 轴。或者,您可以将第一个主元件用作 X 轴和 Y 轴。 【译者 TRHX 注:在使用该方法时要先安装 sklearn 库】 1234567891011121314151617181920212223242526272829303132333435from sklearn.cluster import AgglomerativeClusteringfrom scipy.spatial import ConvexHull# Import Datadf = pd.read_csv('https://raw.githubusercontent.com/selva86/datasets/master/USArrests.csv')# Agglomerative Clusteringcluster = AgglomerativeClustering(n_clusters=5, affinity='euclidean', linkage='ward')cluster.fit_predict(df[['Murder', 'Assault', 'UrbanPop', 'Rape']])# Plotplt.figure(figsize=(14, 10), dpi=80)plt.scatter(df.iloc[:, 0], df.iloc[:, 1], c=cluster.labels_, cmap='tab10')# Encircledef encircle(x, y, ax=None, **kw): if not ax: ax = plt.gca() p = np.c_[x, y] hull = ConvexHull(p) poly = plt.Polygon(p[hull.vertices,:], **kw) ax.add_patch(poly)# Draw polygon surrounding verticesencircle(df.loc[cluster.labels_ == 0, 'Murder'], df.loc[cluster.labels_ == 0, 'Assault'], ec=\"k\", fc=\"gold\", alpha=0.2, linewidth=0)encircle(df.loc[cluster.labels_ == 1, 'Murder'], df.loc[cluster.labels_ == 1, 'Assault'], ec=\"k\", fc=\"tab:blue\", alpha=0.2, linewidth=0)encircle(df.loc[cluster.labels_ == 2, 'Murder'], df.loc[cluster.labels_ == 2, 'Assault'], ec=\"k\", fc=\"tab:red\", alpha=0.2, linewidth=0)encircle(df.loc[cluster.labels_ == 3, 'Murder'], df.loc[cluster.labels_ == 3, 'Assault'], ec=\"k\", fc=\"tab:green\", alpha=0.2, linewidth=0)encircle(df.loc[cluster.labels_ == 4, 'Murder'], df.loc[cluster.labels_ == 4, 'Assault'], ec=\"k\", fc=\"tab:orange\", alpha=0.2, linewidth=0)# Decorationsplt.xlabel('Murder'); plt.xticks(fontsize=12)plt.ylabel('Assault'); plt.yticks(fontsize=12)plt.title('Agglomerative Clustering of USArrests (5 Groups)', fontsize=22)plt.show() 【49】安德鲁斯曲线(Andrews Curve)安德鲁斯曲线有助于可视化是否存在基于给定分组的数值特征的固有分组。如果特征(数据集中的列)不能帮助区分组(cyl),则行将不会像下图所示被很好地分隔开。 12345678910111213141516171819202122from pandas.plotting import andrews_curves# Importdf = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mtcars.csv\")df.drop(['cars', 'carname'], axis=1, inplace=True)# Plotplt.figure(figsize=(12, 9), dpi=80)andrews_curves(df, 'cyl', colormap='Set1')# Lighten bordersplt.gca().spines[\"top\"].set_alpha(0)plt.gca().spines[\"bottom\"].set_alpha(.3)plt.gca().spines[\"right\"].set_alpha(0)plt.gca().spines[\"left\"].set_alpha(.3)plt.title('Andrews Curves of mtcars', fontsize=22)plt.xlim(-3, 3)plt.grid(alpha=0.3)plt.xticks(fontsize=12)plt.yticks(fontsize=12)plt.show() 【50】平行坐标图(Parallel Coordinates)平行坐标有助于可视化功能是否有助于有效地隔离组。如果一个分离受到影响,则该特征可能在预测该组时非常有用。 1234567891011121314151617181920from pandas.plotting import parallel_coordinates# Import Datadf_final = pd.read_csv(\"https://raw.githubusercontent.com/selva86/datasets/master/diamonds_filter.csv\")# Plotplt.figure(figsize=(12, 9), dpi=80)parallel_coordinates(df_final, 'cut', colormap='Dark2')# Lighten bordersplt.gca().spines[\"top\"].set_alpha(0)plt.gca().spines[\"bottom\"].set_alpha(.3)plt.gca().spines[\"right\"].set_alpha(0)plt.gca().spines[\"left\"].set_alpha(.3)plt.title('Parallel Coordinated of Diamonds', fontsize=22)plt.grid(alpha=0.3)plt.xticks(fontsize=12)plt.yticks(fontsize=12)plt.show() 1234这里是一段防爬虫文本,请读者忽略。本译文首发于 CSDN,作者 Selva Prabhakaran,译者 TRHX。本文链接:https://itrhx.blog.csdn.net/article/details/106558131原文链接:https://www.machinelearningplus.com/plots/top-50-matplotlib-visualizations-the-master-plots-python/","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Matplotlib","slug":"Python-数据分析/Matplotlib","permalink":"https://www.itrhx.com/categories/Python-数据分析/Matplotlib/"}],"tags":[{"name":"Matplotlib","slug":"Matplotlib","permalink":"https://www.itrhx.com/tags/Matplotlib/"}]},{"title":"Python 数据分析三剑客之 Matplotlib(十):3D 图的绘制","slug":"A77-Matplotlib-10","date":"2020-06-07T16:01:53.824Z","updated":"2020-08-06T03:23:28.610Z","comments":true,"path":"2020/06/08/A77-Matplotlib-10/","link":"","permalink":"https://www.itrhx.com/2020/06/08/A77-Matplotlib-10/","excerpt":"","text":"Matplotlib 系列文章: Python 数据分析三剑客之 Matplotlib(一):初识 Matplotlib 与其 matplotibrc 配置文件 Python 数据分析三剑客之 Matplotlib(二):文本描述 / 中文支持 / 画布 / 网格等基本图像属性 Python 数据分析三剑客之 Matplotlib(三):图例 / LaTeX / 刻度 / 子图 / 补丁等基本图像属性 Python 数据分析三剑客之 Matplotlib(四):线性图的绘制 Python 数据分析三剑客之 Matplotlib(五):散点图的绘制 Python 数据分析三剑客之 Matplotlib(六):直方图 / 柱状图 / 条形图的绘制 Python 数据分析三剑客之 Matplotlib(七):饼状图的绘制 Python 数据分析三剑客之 Matplotlib(八):等高线 / 等值线图的绘制 Python 数据分析三剑客之 Matplotlib(九):极区图 / 极坐标图 / 雷达图的绘制 Python 数据分析三剑客之 Matplotlib(十):3D 图的绘制 Python 数据分析三剑客之 Matplotlib(十一):最热门最常用的 50 个图表【译文】 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106558131未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【01x00】了解 mplot3d Toolkitmplot3d Toolkit 即 mplot3d 工具包,在 matplotlib 中使用 mplot3d 工具包可以绘制 3D 图。 mplot3d 官方文档:https://matplotlib.org/tutorials/toolkits/mplot3d.html 在 matplotlib 中,figure 为画布,axes 为绘图区,fig.add_subplot()、plt.subplot() 方法均可以创建子图,在绘制 3D 图时,某些 2D 图的参数也适用于 3D 图,在本文的示例中,可能会用到的一些没有具体解释的函数或者参数,其用法均可在前面的系列文章中找到: 《Python 数据分析三剑客之 Matplotlib(一):初识 Matplotlib 与其 matplotibrc 配置文件》 《Python 数据分析三剑客之 Matplotlib(二):文本描述 / 中文支持 / 画布 / 网格等基本图像属性》 《Python 数据分析三剑客之 Matplotlib(三):图例 / LaTeX / 刻度 / 子图 / 补丁等基本图像属性》 绘制 3D 图的步骤:创建 Axes3D 对象,然后调用 Axes3D 的不同方法来绘制不同类型的 3D 图。以下介绍三种 Axes3D 对象创建的方法。 【01x01】Axes3D 对象创建方法一:Axes3D(fig)在 Matplotlib 1.0.0 版本中,绘制 3D 图需要先导入 Axes3D 包,获取 figure 画布对象 fig 后,通过 Axes3D(fig) 方法来创建 Axes3D 对象,具体方法如下: 12345678910111213141516import numpy as npimport matplotlib.pyplot as pltfrom mpl_toolkits.mplot3d import Axes3D# 获取 figure 画布并创建 Axes3D 对象fig = plt.figure()ax = Axes3D(fig)# 数据坐标z = np.linspace(0, 15, 1000)x = np.sin(z)y = np.cos(z)# 绘制线性图ax.plot(x, y, z)plt.show() 【01x02】Axes3D 对象创建方法二:add_subplot在 Matplotlib 3.2.0 版本中,绘制 3D 图可以通过创建子图,然后指定 projection 参数 为 3d 即可,返回的 ax 为 Axes3D 对象,以下两种方法均可: 123456789101112131415import numpy as npimport matplotlib.pyplot as plt# 获取 figure 画布并通过子图创建 Axes3D 对象fig = plt.figure()ax = fig.add_subplot(111, projection='3d')# 数据坐标z = np.linspace(0, 15, 1000)x = np.sin(z)y = np.cos(z)# 绘制线性图ax.plot(x, y, z)plt.show() 1234567891011121314import numpy as npimport matplotlib.pyplot as plt# 通过子图创建 Axes3D 对象ax = plt.subplot(111, projection='3d')# 数据坐标z = np.linspace(0, 15, 1000)x = np.sin(z)y = np.cos(z)# 绘制线性图ax.plot(x, y, z)plt.show() 【01x03】Axes3D 对象创建方法三:gca除了以上两种方法以外,还可以先获取画布对象 fig,再通过 fig.gca() 方法获取当前绘图区(gca = Get Current Axes),然后指定 projection 参数 为 3d 即可,返回的 ax 为 Axes3D 对象。 123456789101112131415import numpy as npimport matplotlib.pyplot as plt# 依次获取画布和绘图区并创建 Axes3D 对象fig = plt.figure()ax = fig.gca(projection='3d')# 数据坐标z = np.linspace(0, 15, 1000)x = np.sin(z)y = np.cos(z)# 绘制线性图ax.plot(x, y, z)plt.show() 以上三种方法运行结果均为下图: 【02x00】cmap 与 colorbar默认情况下,散点图、线性图、曲面图等将以纯色着色,但可以通过提供 cmap 参数支持颜色映射。cmap 参数用于设置一些特殊的颜色组合,如渐变色等,参数取值通常为 Colormap 中的值,具体取值可参见下图: 官方文档:https://matplotlib.org/tutorials/colors/colormaps.html 如果使用了 cmap 参数,则可以使用 pyplot.colorbar() 函数来绘制一个色条,即颜色对照条。 基本语法:matplotlib.pyplot.colorbar([mappable=None, cax=None, ax=None, **kw]) 部分参数解释如下表,其他参数,如长度,宽度等请参考官方文档。 参数 描述 mappable 要设置色条的图像对象,该参数对于 Figure.colorbar 方法是必需的,但对于 pyplot.colorbar 函数是可选的 cax 可选项,要绘制色条的轴 ax 可选项,设置色条的显示位置,通常在一个画布上有多个子图时使用 **kw 可选项,其他关键字参数,参考官方文档 【03x00】3D 线性图:Axes3D.plot基本方法:Axes3D.plot(xs, ys[, zs, zdir='z', *args, **kwargs]) 参数 描述 xs 一维数组,点的 x 轴坐标 ys 一维数组,点的 y 轴坐标 zs 一维数组,可选项,点的 z 轴坐标 zdir 可选项,在 3D 轴上绘制 2D 数据时,数据必须以 xs,ys 的形式传递,若此时将 zdir 设置为 ‘y’,数据将会被绘制到 x-z 轴平面上,默认为 ‘z’ **kwargs 其他关键字参数,可选项,可参见 matplotlib.axes.Axes.plot 12345678910111213141516171819202122232425262728293031323334import numpy as npimport matplotlib.pyplot as plt# 设置中文显示plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']# 依次获取画布和绘图区并创建 Axes3D 对象fig = plt.figure()ax = fig.gca(projection='3d')# 第一条3D线性图数据theta = np.linspace(-4 * np.pi, 4 * np.pi, 100)z1 = np.linspace(-2, 2, 100)r = z1**2 + 1x1 = r * np.sin(theta)y1 = r * np.cos(theta)# 第二条3D线性图数据z2 = np.linspace(-3, 3, 100)x2 = np.sin(z2)y2 = np.cos(z2)# 绘制3D线性图ax.plot(x1, y1, z1, color='b', label='3D 线性图一')ax.plot(x2, y2, z2, color='r', label='3D 线性图二')# 设置标题、轴标签、图例,也可以直接使用 plt.title、plt.xlabel、plt.legend...ax.set_title('绘制 3D 线性图示例', pad=15, fontsize='12')ax.set_xlabel('x 轴', color='r', fontsize='12')ax.set_ylabel('y 轴', color='g', fontsize='12')ax.set_zlabel('z 轴', color='b', fontsize='12')ax.legend()plt.show() 【04x00】3D 散点图:Axes3D.scatter基本方法:Axes3D.scatter(xs, ys[, zs=0, zdir='z', s=20, c=None, depthshade=True, *args, **kwargs]) 参数 描述 xs 一维数组,点的 x 轴坐标 ys 一维数组,点的 y 轴坐标 zs 一维数组,可选项,点的 z 轴坐标 zdir 可选项,在 3D 轴上绘制 2D 数据时,数据必须以 xs,ys 的形式传递,若此时将 zdir 设置为 ‘y’,数据将会被绘制到 x-z 轴平面上,默认为 ‘z’ s 标量或数组类型,可选项,标记的大小,默认 20 c 标记的颜色,可选项,可以是单个颜色或者一个颜色列表支持英文颜色名称及其简写、十六进制颜色码等,更多颜色示例参见官网 Color Demo depthshade bool 值,可选项,默认 True,是否为散点标记着色以提供深度外观 **kwargs 其他关键字参数,可选项,可参见 scatter 1234567891011121314151617181920212223242526272829303132import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']# 依次获取画布和绘图区并创建 Axes3D 对象fig = plt.figure()ax = fig.gca(projection='3d')n = 100def randrange(n, vmin, vmax): return (vmax - vmin)*np.random.rand(n) + vmin'''定义绘制 n 个随机点,设置每一组数据点的样式和范围x轴数据位于[23,32]区间,y轴数据位于[0,100]区间,z轴数据位于[zlow,zhigh]区间'''for m, zlow, zhigh in [('o', -50, -25), ('^', -30, -5)]: xs = randrange(n, 23, 32) ys = randrange(n, 0, 100) zs = randrange(n, zlow, zhigh) ax.scatter(xs, ys, zs, marker=m)# 设置标题、轴标签、图例,也可以直接使用 plt.title、plt.xlabel...ax.set_title('绘制 3D 散点图示例', pad=15, fontsize='12')ax.set_xlabel('x 轴', color='b')ax.set_ylabel('y 轴', color='b')ax.set_zlabel('z 轴', color='b')plt.show() 【05x00】3D 线框图:Axes3D.plot_wireframe基本方法:Axes3D.plot_wireframe(X, Y, Z[, *args, **kwargs]) 参数 描述 X 二维数组,x 轴数据 Y 二维数组,y 轴数据 Z 二维数组,z 轴数据 **kwargs 其他关键字参数,可选项,如线条样式颜色等,可参见 Line3DCollection 1234567891011121314151617181920212223242526272829303132import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']# 获取 figure 画布并通过子图创建 Axes3D 对象fig = plt.figure()ax = fig.add_subplot(111, projection='3d')# 定义Z轴坐标的生成方法def f(m, n): return np.sin(np.sqrt(m ** 2 + n ** 2))# 设置3D线框图数据x = np.linspace(-6, 6, 30)y = np.linspace(-6, 6, 30)# 生成网格点坐标矩阵,该方法在系列文章八中有具体介绍X, Y = np.meshgrid(x, y)Z = f(X, Y)# 绘制3D线框图ax.plot_wireframe(X, Y, Z, color='c')# 设置标题、轴标签、图例,也可以直接使用 plt.title、plt.xlabel...ax.set_title('绘制 3D 线框图示例', pad=15, fontsize='12')ax.set_xlabel('x 轴')ax.set_ylabel('y 轴')ax.set_zlabel('z 轴')plt.show() 【06x00】3D 曲面图:Axes3D.plot_surface基本方法:Axes3D.plot_surface(X, Y, Z[, *args, vmin=None, vmax=None, **kwargs]) 参数 描述 X 二维数组,x 轴数据 Y 二维数组,y 轴数据 Z 二维数组,z 轴数据 vmin / vmax 规定数据界限 **kwargs 其他关键字参数,可选项,如线条样式颜色等,可参见 Line3DCollection 1234567891011121314151617181920212223242526272829303132import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']# 获取 figure 画布并通过子图创建 Axes3D 对象fig = plt.figure()ax = fig.add_subplot(111, projection='3d')# 设置3D曲面图数据X = np.arange(-5, 5, 0.25)Y = np.arange(-5, 5, 0.25)# 生成网格点坐标矩阵,该方法在系列文章八中有具体介绍X, Y = np.meshgrid(X, Y)R = np.sqrt(X**2 + Y**2)Z = np.sin(R)# 绘制3D曲面图并添加色条(长度0.8)surface = ax.plot_surface(X, Y, Z, cmap='rainbow', antialiased=False)fig.colorbar(surface, shrink=0.8)# 设置标题、轴标签、图例,也可以直接使用 plt.title、plt.xlabel...ax.set_title('绘制 3D 曲面图示例', pad=15, fontsize='12')ax.set_xlabel('x 轴')ax.set_ylabel('y 轴')ax.set_zlabel('z 轴')# 调整观察角度和方位角,俯仰角25度,方位角40度ax.view_init(25, 40)# 设置Z轴刻度界限ax.set_zlim(-2, 2)plt.show() 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106558131未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【07x00】3D 柱状图:Axes3D.bar基本方法:Axes3D.bar(left, height, zs=0, zdir='z', *args, **kwargs) 参数 描述 left 一维数组,柱状图最左侧位置的 x 坐标 height 一维数组,柱状图的高度(y 坐标) zs 第 i 个多边形将出现在平面 y=zs[i] 上 zdir 可选项,在 3D 轴上绘制 2D 数据时,数据必须以 xs,ys 的形式传递,若此时将 zdir 设置为 ‘y’,数据将会被绘制到 x-z 轴平面上,默认为 ‘z’ **kwargs 其他关键字参数,参见 matplotlib.axes.Axes.bar 123456789101112131415161718192021222324252627import matplotlib.pyplot as pltimport numpy as npplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']# 获取 figure 画布并通过子图创建 Axes3D 对象fig = plt.figure()ax = fig.add_subplot(111, projection='3d')colors = ['r', 'g', 'b', 'y']yticks = [3, 2, 1, 0]# 设置3D柱状图数据并绘制图像for c, k in zip(colors, yticks): xs = np.arange(20) ys = np.random.rand(20) cs = [c] * len(xs) ax.bar(xs, ys, zs=k, zdir='y', color=cs, alpha=0.8)# 设置图像标题、坐标标签以及范围ax.set_title('绘制 3D 柱状图示例', pad=15, fontsize='12')ax.set_xlabel('X 轴')ax.set_ylabel('Y 轴')ax.set_zlabel('Z 轴')ax.set_yticks(yticks)plt.show() 【08x00】3D 箭头图:Axes3D.quiver基本方法:Axes3D.quiver(X, Y, Z, U, V, W, length=1, arrow_length_ratio=0.3, pivot='tail', normalize=False, **kwargs) 参数 描述 X, Y, Z 数组形式,箭头位置的 x、y 和 z 轴坐标(默认为箭头尾部) U, V, W 数组形式,箭头向量的 x、y 和 z 轴分量 length float 类型,每个箭筒的长度,默认为 1.0 arrow_length_ratio float 类型,箭头相对于箭身的比率,默认为 0.3 pivot 箭头在网格点上的位置;箭头围绕该点旋转,因此命名为 pivot,默认为 ‘tail’可选项:'tail':尾部;'middle':中间;'tip':尖端 normalize bool 类型,如果为 True,则所有箭头的长度都将相同默认为 False,即箭头的长度取决于 U、V、W 的值 **kwargs 其他关键字参数,参见 LineCollection 1234567891011121314151617181920212223242526272829303132import matplotlib.pyplot as pltimport numpy as npplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']# 依次获取画布和绘图区并创建 Axes3D 对象fig = plt.figure()ax = fig.gca(projection='3d')# 设置箭头位置x, y, z = np.meshgrid(np.arange(-0.8, 1, 0.2), np.arange(-0.8, 1, 0.2), np.arange(-0.8, 1, 0.8))# 设置箭头数据u = np.sin(np.pi * x) * np.cos(np.pi * y) * np.cos(np.pi * z)v = -np.cos(np.pi * x) * np.sin(np.pi * y) * np.cos(np.pi * z)w = (np.sqrt(2.0 / 3.0) * np.cos(np.pi * x) * np.cos(np.pi * y) * np.sin(np.pi * z))# 绘制 3D 箭头图ax.quiver(x, y, z, u, v, w, length=0.1, normalize=True)# 设置图像标题、坐标标签ax.set_title('绘制 3D 箭头图示例', pad=15, fontsize='12')ax.set_xlabel('X 轴')ax.set_ylabel('Y 轴')ax.set_zlabel('Z 轴')# 调整观察角度,俯仰角20度ax.view_init(20)plt.show() 【09x00】3D 等高线图:Axes3D.contour基本方法:Axes3D.contour(X, Y, Z[, *args, extend3d=False, stride=5, zdir='z', offset=None, **kwargs]) 参数 描述 X 一维数组,x 轴数据 Y 一维数组,y 轴数据 Z 一维数组,z 轴数据 extend3d bool 值,可选项,是否以 3D 延伸轮廓,默认 False stride int 类型,可选项,用于延伸轮廓的步长 zdir 可选项,在 3D 轴上绘制 2D 数据时,数据必须以 xs,ys 的形式传递,若此时将 zdir 设置为 ‘y’,数据将会被绘制到 x-z 轴平面上,默认为 ‘z’ offset 标量,可选项,如果指定,则在垂直于 zdir 的平面上的位置绘制轮廓线的投影 **kwargs 其他关键字参数,可选项,可参见 matplotlib.axes.Axes.contour 1234567891011121314151617181920212223242526272829303132import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']# 获取 figure 画布并通过子图创建 Axes3D 对象fig = plt.figure(figsize=(8, 4.8))ax = fig.add_subplot(111, projection='3d')# 设置等高线数据X = np.arange(-2.0, 2.0, 0.01)Y = np.arange(-2.0, 2.0, 0.01)# 生成网格点坐标矩阵m, n = np.meshgrid(X, Y)# 指定一个函数用于计算每个点的高度,也可以直接使用二维数组储存每个点的高度def f(a, b): return (1 - b ** 5 + a ** 5) * np.exp(-a ** 2 - b ** 2)# 绘制3D等高线图并添加色条图(长度0.8)contour = ax.contour(X, Y, f(m, n), cmap='rainbow')fig.colorbar(contour, shrink=0.8)# 设置标题、轴标签、图例,也可以直接使用 plt.title、plt.xlabel...ax.set_title('绘制 3D 等高线图示例', pad=15, fontsize='12')ax.set_xlabel('x 轴')ax.set_ylabel('y 轴')ax.set_zlabel('z 轴')plt.show() 【10x00】3D 等高线填充图:Axes3D.contourf基本语法:Axes3D.contourf(X, Y, Z[, *args, zdir='z', offset=None, **kwargs]) 参数 描述 X 一维数组,x 轴数据 Y 一维数组,y 轴数据 Z 一维数组,z 轴数据 zdir 可选项,在 3D 轴上绘制 2D 数据时,数据必须以 xs,ys 的形式传递,若此时将 zdir 设置为 ‘y’,数据将会被绘制到 x-z 轴平面上,默认为 ‘z’ offset 标量,可选项,如果指定,则在垂直于 zdir 的平面上的位置绘制轮廓线的投影 **kwargs 其他关键字参数,可选项,可参见 matplotlib.axes.Axes.contourf 1234567891011121314151617181920212223242526272829303132import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']# 获取 figure 画布并通过子图创建 Axes3D 对象fig = plt.figure(figsize=(8, 4.8))ax = fig.add_subplot(111, projection='3d')# 设置等高线数据X = np.arange(-2.0, 2.0, 0.01)Y = np.arange(-2.0, 2.0, 0.01)# 生成网格点坐标矩阵m, n = np.meshgrid(X, Y)# 指定一个函数用于计算每个点的高度,也可以直接使用二维数组储存每个点的高度def f(a, b): return (1 - b ** 5 + a ** 5) * np.exp(-a ** 2 - b ** 2)# 绘制3D等高线图并添加色条图(长度0.8)contourf = ax.contourf(X, Y, f(m, n), cmap='rainbow')fig.colorbar(contourf, shrink=0.8)# 设置标题、轴标签、图例,也可以直接使用 plt.title、plt.xlabel...ax.set_title('绘制 3D 等高线填充图示例', pad=15, fontsize='12')ax.set_xlabel('x 轴')ax.set_ylabel('y 轴')ax.set_zlabel('z 轴')plt.show() 【11x00】3D 三角曲面图:Axes3D.plot_trisurf基本方法:Axes3D.plot_trisurf(X, Y, Z[, *args, color=None, vmin=None, vmax=None, **kwargs]) 参数 描述 X 一维数组,x 轴数据 Y 一维数组,y 轴数据 Z 一维数组,z 轴数据 color 曲面表面的颜色 vmin / vmax 规定数据界限 **kwargs 可选项,其他关键字参数,可参见 Poly3DCollection 1234567891011121314151617181920212223242526272829import matplotlib.pyplot as pltimport numpy as npplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']# 获取 figure 画布并通过子图创建 Axes3D 对象fig = plt.figure()ax = fig.add_subplot(111, projection='3d')n_radii = 8n_angles = 36radii = np.linspace(0.125, 1.0, n_radii)angles = np.linspace(0, 2*np.pi, n_angles, endpoint=False)[..., np.newaxis]x = np.append(0, (radii*np.cos(angles)).flatten())y = np.append(0, (radii*np.sin(angles)).flatten())z = np.sin(-x*y)# 绘制3D三角曲面图并添加色条(长度0.8)trisurf = ax.plot_trisurf(x, y, z, cmap='rainbow')fig.colorbar(trisurf, shrink=0.8)# 设置标题、轴标签、图例,也可以直接使用 plt.title、plt.xlabel...ax.set_title('绘制 3D 三角曲面图示例', pad=15, fontsize='12')ax.set_xlabel('x 轴')ax.set_ylabel('y 轴')ax.set_zlabel('z 轴')plt.show() 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364import numpy as npimport matplotlib.pyplot as pltimport matplotlib.tri as mtriplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']fig = plt.figure(figsize=(15, 6))# ============ 第一个示例图 ============ #ax = fig.add_subplot(1, 2, 1, projection='3d')u = np.linspace(0, 2.0 * np.pi, endpoint=True, num=50)v = np.linspace(-0.5, 0.5, endpoint=True, num=10)u, v = np.meshgrid(u, v)u, v = u.flatten(), v.flatten()x = (1 + 0.5 * v * np.cos(u / 2.0)) * np.cos(u)y = (1 + 0.5 * v * np.cos(u / 2.0)) * np.sin(u)z = 0.5 * v * np.sin(u / 2.0)tri = mtri.Triangulation(u, v)trisurf_1 = ax.plot_trisurf(x, y, z, triangles=tri.triangles, cmap='cool')fig.colorbar(trisurf_1, shrink=0.8)ax.set_zlim(-1, 1)ax.set_title('绘制 3D 三角曲面图示例一', pad=15, fontsize='12')ax.set_xlabel('x 轴')ax.set_ylabel('y 轴')ax.set_zlabel('z 轴')# ============ 第二个示例图 ============ #ax = fig.add_subplot(1, 2, 2, projection='3d')n_angles = 36n_radii = 8min_radius = 0.25radii = np.linspace(min_radius, 0.95, n_radii)angles = np.linspace(0, 2*np.pi, n_angles, endpoint=False)angles = np.repeat(angles[..., np.newaxis], n_radii, axis=1)angles[:, 1::2] += np.pi/n_anglesx = (radii*np.cos(angles)).flatten()y = (radii*np.sin(angles)).flatten()z = (np.cos(radii)*np.cos(3*angles)).flatten()triang = mtri.Triangulation(x, y)xmid = x[triang.triangles].mean(axis=1)ymid = y[triang.triangles].mean(axis=1)mask = xmid**2 + ymid**2 < min_radius**2triang.set_mask(mask)trisurf_2 = ax.plot_trisurf(triang, z, cmap='hsv')fig.colorbar(trisurf_2, shrink=0.8)ax.set_title('绘制 3D 三角曲面图示例二', pad=15, fontsize='12')ax.set_xlabel('x 轴')ax.set_ylabel('y 轴')ax.set_zlabel('z 轴')plt.show() 【12x00】将 2D 图像聚合到 3D 图像中:Axes3D.add_collection3d基本方法:Axes3D.add_collection3d(col, zs=0, zdir='z') 参数 描述 col PolyCollection / LineCollection / PatchCollection 对象 zs 第 i 个多边形将出现在平面 y=zs[i] 上 zdir 可选项,在 3D 轴上绘制 2D 数据时,数据必须以 xs,ys 的形式传递,若此时将 zdir 设置为 ‘y’,数据将会被绘制到 x-z 轴平面上,默认为 ‘z’ 该函数一般用来向图形中添加 3D 集合对象,以下用一个示例来展示某个地区在不同年份和不同月份的降水量: 12345678910111213141516171819202122232425262728293031import numpy as npimport matplotlib.pyplot as pltfrom matplotlib.collections import PolyCollectionplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']fig = plt.figure()ax = fig.gca(projection='3d')np.random.seed(59)month = np.arange(0, 13)years = [2017, 2018, 2019, 2020]precipitation = []for year in years: value = np.random.rand(len(month)) * 300 value[0], value[-1] = 0, 0 precipitation.append(list(zip(month, value)))poly = PolyCollection(precipitation, facecolors=['r', 'g', 'b', 'y'], alpha=.6)ax.add_collection3d(poly, zs=years, zdir='y')ax.set_title('2D 图像聚合到 3D 图像示例', pad=15, fontsize='12')ax.set_xlabel('月份')ax.set_ylabel('年份')ax.set_zlabel('降水量')ax.set_xlim3d(0, 12)ax.set_ylim3d(2016, 2021)ax.set_zlim3d(0, 300)plt.show() 此外,该方法也常被用于绘制 3D 多边形图,即多边体,示例如下: 1234567891011121314151617181920212223242526272829303132import matplotlib.pyplot as pltfrom mpl_toolkits.mplot3d.art3d import Poly3DCollection, Line3DCollectionplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']fig = plt.figure()ax = fig.gca(projection='3d')# 六面体顶点和面verts = [(0, 0, 0), (0, 1, 0), (1, 1, 0), (1, 0, 0), (0, 0, 1), (0, 1, 1), (1, 1, 1), (1, 0, 1)]faces = [[0, 1, 2, 3], [4, 5, 6, 7], [0, 1, 5, 4], [1, 2, 6, 5], [2, 3, 7, 6], [0, 3, 7, 4]]# 获取每个面的顶点poly3d = [[verts[vert_id] for vert_id in face] for face in faces]# 绘制顶点x, y, z = zip(*verts)ax.scatter(x, y, z)# 绘制多边形面ax.add_collection3d(Poly3DCollection(poly3d, facecolors='w', linewidths=1, alpha=0.5))# 绘制多边形的边ax.add_collection3d(Line3DCollection(poly3d, colors='k', linewidths=0.5, linestyles=':'))# 设置图像标题、坐标标签以及范围ax.set_title('绘制多边体示例', pad=15, fontsize='12')ax.set_xlabel('X 轴')ax.set_ylabel('Y 轴')ax.set_zlabel('Z 轴')ax.set_xlim3d(-0.5, 1.5)ax.set_ylim3d(-0.5, 1.5)ax.set_zlim3d(-0.5, 1.5)plt.show() 【13x00】3D 图添加文本描述:Axes3D.text基本方法:Axes3D.text(x, y, z, s[, zdir=None, **kwargs]) 参数 描述 x, y, z 文本位置的 x、y、z 轴坐标 s 要添加的文本 zdir 可选项,若将 zdir 设置为 ‘y’,文本将会被投影到 x-z 轴平面上,默认为 None **kwargs 其他关键字参数,参见 matplotlib.text 123456789101112131415161718192021222324252627282930313233import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']# 依次获取画布和绘图区并创建 Axes3D 对象fig = plt.figure()ax = fig.gca(projection='3d')# Demo 1: zdir 参数用法zdirs = (None, 'x', 'y', 'z', (1, 1, 0), (1, 1, 1))xs = (1, 4, 4, 9, 4, 1)ys = (2, 5, 8, 10, 1, 2)zs = (10, 3, 8, 9, 1, 8)for zdir, x, y, z in zip(zdirs, xs, ys, zs): label = '(%d, %d, %d), dir=%s' % (x, y, z, zdir) ax.text(x, y, z, label, zdir)# Demo 2:设置颜色ax.text(9, 0, 0, \"red\", color='red')# Demo 3: text2D,位置(0,0)为左下角,(1,1)为右上角。ax.text2D(0.05, 0.95, \"2D Text\", transform=ax.transAxes)# 设置坐标轴界限和标签ax.set_xlim(0, 10)ax.set_ylim(0, 10)ax.set_zlim(0, 10)ax.set_xlabel('X 轴')ax.set_ylabel('Y 轴')ax.set_zlabel('Z 轴')plt.show() 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106558131未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Matplotlib","slug":"Python-数据分析/Matplotlib","permalink":"https://www.itrhx.com/categories/Python-数据分析/Matplotlib/"}],"tags":[{"name":"Matplotlib","slug":"Matplotlib","permalink":"https://www.itrhx.com/tags/Matplotlib/"},{"name":"3D图","slug":"3D图","permalink":"https://www.itrhx.com/tags/3D图/"}]},{"title":"Python 数据分析三剑客之 Matplotlib(九):极区图/极坐标图/雷达图的绘制","slug":"A76-Matplotlib-09","date":"2020-06-03T11:00:14.070Z","updated":"2020-08-06T03:21:28.276Z","comments":true,"path":"2020/06/03/A76-Matplotlib-09/","link":"","permalink":"https://www.itrhx.com/2020/06/03/A76-Matplotlib-09/","excerpt":"","text":"Matplotlib 系列文章: Python 数据分析三剑客之 Matplotlib(一):初识 Matplotlib 与其 matplotibrc 配置文件 Python 数据分析三剑客之 Matplotlib(二):文本描述 / 中文支持 / 画布 / 网格等基本图像属性 Python 数据分析三剑客之 Matplotlib(三):图例 / LaTeX / 刻度 / 子图 / 补丁等基本图像属性 Python 数据分析三剑客之 Matplotlib(四):线性图的绘制 Python 数据分析三剑客之 Matplotlib(五):散点图的绘制 Python 数据分析三剑客之 Matplotlib(六):直方图 / 柱状图 / 条形图的绘制 Python 数据分析三剑客之 Matplotlib(七):饼状图的绘制 Python 数据分析三剑客之 Matplotlib(八):等高线 / 等值线图的绘制 Python 数据分析三剑客之 Matplotlib(九):极区图 / 极坐标图 / 雷达图的绘制 Python 数据分析三剑客之 Matplotlib(十):3D 图的绘制 Python 数据分析三剑客之 Matplotlib(十一):最热门最常用的 50 个图表【译文】 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106162412未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【1x00】了解极坐标参考百度百科:极坐标,属于二维坐标系统,创始人是牛顿,主要应用于数学领域。极坐标是指在平面内取一个定点 O,叫极点,引一条射线 Ox,叫做极轴,再选定一个长度单位和角度的正方向(通常取逆时针方向)。对于平面内任何一点 M,用 ρ 表示线段 OM 的长度(有时也用 r 表示),θ 表示从 Ox 到 OM 的角度,ρ 叫做点 M 的极径,θ 叫做点 M 的极角,有序数对 (ρ,θ) 就叫点 M 的极坐标,这样建立的坐标系叫做极坐标系。通常情况下,M 的极径坐标单位为 1(长度单位),极角坐标单位为 rad(或°)。 【2x00】基本方法 matplotlib.pyplot.polar()matplotlib.pyplot.polar() 方法可用于绘制极坐标图。 基本语法:polar(theta, r, **kwargs) theta:点的角坐标,以弧度单位传入参数; r:点的半径坐标; **kwargs:可选项,其他 Line2D 属性,常用属性见表一。 拓展:数学上通常是用弧度而非角度,弧度单位缩写为 rad,2π rad = 360°,1° ≈ 0.0174533 rad,1 rad ≈ 57.29578°。 角度转换为弧度公式:弧度 = 角度 ÷ 180 × π 弧度转换为角度公式:角度 = 弧度 × 180 ÷ π 表一:Line2D 部分属性,完整属性参见官方文档:https://matplotlib.org/api/_as_gen/matplotlib.lines.Line2D.html 属性 描述 alpha 线条透明度,float 类型,取值范围:[0, 1],默认为 1.0,即不透明 antialiased / aa 是否使用抗锯齿渲染,默认为 True color / c 线条颜色,支持英文颜色名称及其简写、十六进制颜色码等,更多颜色示例参见官网 Color Demo fillstyle 点的填充样式,'full'、'left'、'right'、'bottom'、'top'、'none' label 图例,具体参数参见:《Python 数据分析三剑客之 Matplotlib(三):图例 / LaTeX / 刻度 / 子图 / 补丁等基本图像属性》 linestyle / ls 连接的线条样式:'-' or 'solid', '--' or 'dashed', '-.' or 'dashdot' ':' or 'dotted', 'none' or ' ' or '' linewidth / lw 连接的线条宽度,float 类型,默认 0.8 marker 标记样式,具体样式参见表二 markeredgecolor / mec marker 标记的边缘颜色 markeredgewidth / mew marker 标记的边缘宽度 markerfacecolor / mfc marker 标记的颜色 markerfacecoloralt / mfcalt marker 标记的备用颜色 markersize / ms marker 标记的大小 表二:marker 标记的样式,官方文档:https://matplotlib.org/api/markers_api.html 标记 描述 "." 点 "," 像素点 "o" 圆圈 "v" 倒三角 "^" 正三角 "<" 左三角 ">" 右三角 "1" 倒三叉星 "2" 正三叉星(类似奔驰车标形状) "3" 左三叉星 "4" 右三叉星 "8" 八边形 "s" 正方形 "p" 五边形 "P" 填充的加号(粗加号) "+" 加号 "*" 星形 "h" 六边形(底部是角) "H" 六边形(底部是边) "x" x 号 "X" 填充的 x 号(粗 x 号) "D" 粗菱形(对角线相等) "d" 细菱形(对角线不等) `” “` 垂直线 "_" 水平线 0 水平线靠左 1 水平线靠右 2 垂直线靠上 3 垂直线靠下 4 左三角(比 "<" 更细) 5 右三角(比 ">" 更细) 6 正三角(比 "^" 更细) 7 倒三角(比 "v" 更细) 8 左三角(比 "<" 更细,靠左显示) 9 右三角(比 ">" 更细,靠右显示) 10 正三角(比 "^" 更细,靠上显示) 11 倒三角(比 "v" 更细,靠下显示) "None" / " " / "" 无样式 '$...$' 支持 LaTeX 数学公式,表达式用美元符号包围起来 【3x00】绘制极坐标1234567891011121314151617181920212223242526272829import numpy as npimport matplotlib.pyplot as plt# 设置中文显示plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']# 设置画布大小plt.figure(figsize=(8.0, 6.0))# 设置三个数据,theta 为点位置的弧度参数,r 为点的半径坐标theta1 = np.array([1.25*np.pi, np.pi/2, 0])theta2 = np.array([-np.pi/6, -np.pi/2, 0, np.pi/2, np.pi])theta3 = np.arange(0., 2*np.pi, 0.5)r1 = np.array([4, 2, 3])r2 = np.array([5, 2, 4, 5, 3])r3 = np.random.randint(0, 5, 13)# 绘制第一个极坐标图,点的标记样式为细菱形,大小为8,点之间的连接线条样式为:plt.polar(theta1, r1, marker='d', ms=8, ls=':', label='数据一')# 填充第一个极坐标图,填充颜色为蓝色,透明度0.3plt.fill(theta1, r1, color='b', alpha=0.3)# 绘制第二个极坐标图,marker、linestyle、color 三个参数可以组合以字符串形式传入plt.polar(theta2, r2, '*-g', ms=10, label='数据二')# 绘制第三个极坐标图,设置 linestyle 为 none,即点与点之间不相连plt.polar(theta3, r3, marker='o', ls='none', ms=8, color='r', label='数据三')plt.title('matplotlib.pyplot.polar 用法示例', pad=25, fontsize=15)plt.legend(bbox_to_anchor=(1.3, 1))plt.show() 示例中 figure、title、legend 等其他方法的解释可参见我的系列文章: 《Python 数据分析三剑客之 Matplotlib(一):初识 Matplotlib 与其 matplotibrc 配置文件》 《Python 数据分析三剑客之 Matplotlib(二):文本描述 / 中文支持 / 画布 / 网格等基本图像属性》 《Python 数据分析三剑客之 Matplotlib(三):图例 / LaTeX / 刻度 / 子图 / 补丁等基本图像属性》 绘制结果如下图: 【4x00】绘制雷达图雷达图是以从同一点开始的轴上表示的三个或更多个定量变量的二维图表的形式显示多变量数据的图形方法。轴的相对位置和角度通常是无信息的。 雷达图也称为网络图,蜘蛛图,星图,蜘蛛网图,不规则多边形,极坐标图或 Kiviat 图。它相当于平行坐标图,轴径向排列。 在前面的示例中,使用了 matplotlib.pyplot.fill() 方法对三个极坐标点围成的图形进行了填充,这就有点儿接近于雷达图了,仔细观察前面的示例,在填充时第一个点和最后一个点之间没有连线,即没有闭合,而更精确的雷达图应该是闭合的,且外围应该是文字描述而不是度数。 在绘制雷达图之前需要提前了解一些函数。这些函数可以帮助我们实现闭合、自定义文字标签等。 【4x01】理解 numpy.concatenate()numpy.concatenate() 方法用于沿现有轴连接一系列数组,我们可以利用此方法来实现闭合操作。 基本语法:numpy.concatenate((a1, a2, ...)[, axis=0, out=None]) 参数 描述 a1, a2, … 要连接的数组,必须拥有相同的维度 axis 沿指定轴连接数组,可选项,如果 axis 为 None,则数组在使用前被展平,默认值为 0 out 用于接收连接后的数组,可选项 用法示例: 12345import numpy as npa = np.array([1, 2, 3, 4])b = np.array(['a', 'b', 'c', 'd'])print(np.concatenate((a, b))) 输出结果如下: 1['1' '2' '3' '4' 'a' 'b' 'c' 'd'] 如果要实现数组的闭合,则可以传入原数组和一个新数组,其中新数组中的元素为原数组中的第一个元素,示例如下: 1234import numpy as npa = np.array([1, 2, 3, 4])print(np.concatenate((a, [a[0]]))) 输出结果如下: 1[1 2 3 4 1] 【4x02】理解 pyplot.thetagrids()matplotlib.pyplot.thetagrids() 方法用于获取并设置当前极区图上的极轴。 基本语法:matplotlib.pyplot.thetagrids(angles, labels=None, fmt=None, **kwargs) 参数 描述 angles 网格线的角度,浮点数、度数组成的元组 labels 每个极轴要使用的文本标签,字符串组成的元组 fmt 格式化 angles 参数,如 '%1.2f' 保留两位小数,注意,将使用以弧度为单位的角度 **kwargs 其他关键字参数,参见官方文档 应用举例: 1234567891011 import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']plt.polar()angles = range(0, 360, 45)labels = ('东', '东北', '北', '西北', '西', '西南', '南', '东南')plt.thetagrids(angles, labels)plt.title('matplotlib.pyplot.thetagrids() 用法示例', pad=15)plt.show() 【4x03】绘制雷达图numpy.concatenate() 方法能够解决闭合问题,matplotlib.pyplot.thetagrids() 能够解决自定义极轴和极轴的文本标记问题,因此就可以绘制一个标准的雷达图了。示例如下: 1234567891011121314151617181920212223242526272829303132import numpy as npimport matplotlib.pyplot as plt# 设置中文显示、画布大小plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']plt.figure(figsize=(8.0, 6.0))# 分割圆并执行闭合操作(0-2π之间返回间隔均匀的6个弧度:π/3、2π/3、π、4π/3、5π/3、2π)theta = np.linspace(0, 2*np.pi, 6, endpoint=False)theta = np.concatenate((theta, [theta[0]]))# 设置两组数据并执行闭合操作data1 = np.array([9, 4, 3, 5, 2, 8])data2 = np.array([3, 6, 9, 6, 3, 2])data1 = np.concatenate((data1, [data1[0]]))data2 = np.concatenate((data2, [data2[0]]))# 绘制并填充两组数据plt.polar(theta, data1, 'bo-', label='小王')plt.polar(theta, data2, 'ro:', label='小张')plt.fill(theta, data1, color='b', alpha=0.3)plt.fill(theta, data2, color='r', alpha=0.3)# 将六个弧度(π/3、2π/3、π、4π/3、5π/3、2π)转换成角度,并分别设置标签labels = np.array(['Python', 'Golang', 'Java', 'C++', 'PHP', 'JavaScript'])plt.thetagrids(theta * 180/np.pi, labels)# 设置刻度范围、标题、图例plt.ylim(0, 10)plt.title('编程语言掌握程度')plt.legend(bbox_to_anchor=(1.3, 1))plt.show() 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106162412未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【5x00】高级用法:绘制极坐标散点图matplotlib.pyplot.polar() 方法可以实现极坐标散点图,但仅用这一个函数的话实现的样式效果并不多,以下介绍另外三种绘制极坐标散点图的方法: matplotlib.pyplot.polar() 和 matplotlib.pyplot.scatter() 结合,前者绘制极坐标图,后者在极坐标图上绘制散点图; matplotlib.pyplot.subplot() 和 matplotlib.pyplot.scatter() 结合,前者添加子图,其中指定 projection='polar' 即为极坐标图, 后者在极坐标图上绘制散点图; matplotlib.pyplot.axes() 与 matplotlib.pyplot.scatter() 结合,前者设置绘图区参数,其中指定 projection='polar' 或 polar=True 即为极坐标图, 后者在极坐标图上绘制散点图。 【5x01】方法一:pyplot.scatter() 与 pyplot.polar()以下用到的 matplotlib.pyplot.scatter() 函数,各参数含义以及支持的其他参数可以参见前文: 《Python 数据分析三剑客之 Matplotlib(二):文本描述 / 中文支持 / 画布 / 网格等基本图像属性》 《Python 数据分析三剑客之 Matplotlib(五):散点图的绘制》 12345678910111213141516import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']N = 50r = 2 * np.random.rand(N)theta = 2 * np.pi * np.random.rand(N)size = 200 * r ** 2colors = N * np.random.rand(N)plt.polar()plt.scatter(theta, r, s=size, c=colors, alpha=0.8)plt.title('极坐标散点图示例一', pad=15)plt.show() 【5x02】方法二:pyplot.scatter() 与 pyplot.subplot()matplotlib.pyplot.subplot() 方法用于添加子图,如果想要子图为极坐标图,则需要指定 projection 参数为 polar,有关此函数的具体介绍可参见官方文档。其他函数的参数解释可参考前文: 《Python 数据分析三剑客之 Matplotlib(三):图例 / LaTeX / 刻度 / 子图 / 补丁等基本图像属性》 《Python 数据分析三剑客之 Matplotlib(五):散点图的绘制》 1234567891011121314151617import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']N = 50r = 2 * np.random.rand(N)theta = 2 * np.pi * np.random.rand(N)size = 200 * r ** 2colors = N * np.random.rand(N)# 一行一列第一个子图plt.subplot(111, projection='polar')plt.scatter(theta, r, s=size, c=colors, alpha=0.8)plt.title('极坐标散点图示例二', pad=15)plt.show() 【5x03】方法三:pyplot.scatter() 与 pyplot.axes()axes 为 Matplotlib 图像中的绘图区,matplotlib.pyplot.axes() 方法可以对绘图区进行设置,同样的也可以设置 projection 参数为 polar 来实现极坐标图,设置 polar=True 也行。示例中其他函数的参数解释可参考前文: 《Python 数据分析三剑客之 Matplotlib(一):初识 Matplotlib 与其 matplotibrc 配置文件》 《Python 数据分析三剑客之 Matplotlib(五):散点图的绘制》 1234567891011121314151617import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']N = 50r = 2 * np.random.rand(N)theta = 2 * np.pi * np.random.rand(N)size = 200 * r ** 2colors = N * np.random.rand(N)# plt.axes(polar=True)plt.axes(projection='polar')plt.scatter(theta, r, s=size, c=colors, alpha=0.8)plt.title('极坐标散点图示例三', pad=15)plt.show() 【6x00】高级用法:绘制极坐标柱状图和极坐标散点图的绘制类似,matplotlib.pyplot.polar() 方法可以实现极坐标图,但仅用这一个函数的话实现的样式效果并不多,以下介绍另外三种绘制极坐标柱状图的方法: matplotlib.pyplot.polar() 和 matplotlib.pyplot.bar() 结合,前者绘制极坐标图,后者在极坐标图上绘制柱状图; matplotlib.pyplot.subplot() 和 matplotlib.pyplot.bar() 结合,前者添加子图,其中指定 projection='polar' 即为极坐标图, 后者在极坐标图上绘制柱状图; matplotlib.pyplot.axes() 与 matplotlib.pyplot.bar() 结合,前者设置绘图区参数,其中指定 projection='polar' 或 polar=True 即为极坐标图, 后者在极坐标图上绘制柱状图。 【6x01】方法一:pyplot.bar() 与 pyplot.polar()以下用到的 matplotlib.pyplot.bar() 函数,各参数含义以及支持的其他参数可以参见前文: 《Python 数据分析三剑客之 Matplotlib(二):文本描述 / 中文支持 / 画布 / 网格等基本图像属性》 《Python 数据分析三剑客之 Matplotlib(六):直方图 / 柱状图 / 条形图的绘制》 1234567891011121314import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']r = np.random.rand(8)theta = np.arange(0, 2 * np.pi, 2 * np.pi / 8)colors = np.array(['#4bb2c5', '#c5b47f', '#EAA228', '#579575', '#839557', '#958c12', '#953579', '#4b5de4'])plt.polar()plt.bar(theta, r, color=colors, alpha=0.8)plt.title('极坐标柱状图示例一', pad=15)plt.show() 【6x02】方法二:pyplot.bar() 与 pyplot.subplot()matplotlib.pyplot.subplot() 方法用于添加子图,如果想要子图为极坐标图,则需要指定 projection 参数为 polar,有关此函数的具体介绍可参见官方文档。其他函数的参数解释可参考前文: 《Python 数据分析三剑客之 Matplotlib(三):图例 / LaTeX / 刻度 / 子图 / 补丁等基本图像属性》 《Python 数据分析三剑客之 Matplotlib(六):直方图 / 柱状图 / 条形图的绘制》 1234567891011121314import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']r = np.random.rand(8)theta = np.arange(0, 2 * np.pi, 2 * np.pi / 8)colors = np.array(['#4bb2c5', '#c5b47f', '#EAA228', '#579575', '#839557', '#958c12', '#953579', '#4b5de4'])plt.subplot(111, projection='polar')plt.bar(theta, r, color=colors, alpha=0.8)plt.title('极坐标柱状图示例二', pad=15)plt.show() 【6x03】方法三:pyplot.bar() 与 pyplot.axes()axes 为 Matplotlib 图像中的绘图区,matplotlib.pyplot.axes() 方法可以对绘图区进行设置,同样的也可以设置 projection 参数为 polar 来实现极坐标图,设置 polar=True 也行。示例中其他函数的参数解释可参考前文: 《Python 数据分析三剑客之 Matplotlib(一):初识 Matplotlib 与其 matplotibrc 配置文件》 《Python 数据分析三剑客之 Matplotlib(六):直方图 / 柱状图 / 条形图的绘制》 123456789101112131415import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']r = np.random.rand(8)theta = np.arange(0, 2 * np.pi, 2 * np.pi / 8)colors = np.array(['#4bb2c5', '#c5b47f', '#EAA228', '#579575', '#839557', '#958c12', '#953579', '#4b5de4'])# plt.axes(polar=True)plt.axes(projection='polar')plt.bar(theta, r, color=colors, alpha=0.8)plt.title('极坐标柱状图示例三', pad=15)plt.show() 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106162412未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Matplotlib","slug":"Python-数据分析/Matplotlib","permalink":"https://www.itrhx.com/categories/Python-数据分析/Matplotlib/"}],"tags":[{"name":"Matplotlib","slug":"Matplotlib","permalink":"https://www.itrhx.com/tags/Matplotlib/"},{"name":"极区图","slug":"极区图","permalink":"https://www.itrhx.com/tags/极区图/"},{"name":"极坐标图","slug":"极坐标图","permalink":"https://www.itrhx.com/tags/极坐标图/"},{"name":"雷达图","slug":"雷达图","permalink":"https://www.itrhx.com/tags/雷达图/"}]},{"title":"Python 数据分析三剑客之 Matplotlib(八):等高线/等值线图的绘制","slug":"A75-Matplotlib-08","date":"2020-04-29T17:28:26.784Z","updated":"2020-08-06T03:19:41.341Z","comments":true,"path":"2020/04/30/A75-Matplotlib-08/","link":"","permalink":"https://www.itrhx.com/2020/04/30/A75-Matplotlib-08/","excerpt":"","text":"Matplotlib 系列文章: Python 数据分析三剑客之 Matplotlib(一):初识 Matplotlib 与其 matplotibrc 配置文件 Python 数据分析三剑客之 Matplotlib(二):文本描述 / 中文支持 / 画布 / 网格等基本图像属性 Python 数据分析三剑客之 Matplotlib(三):图例 / LaTeX / 刻度 / 子图 / 补丁等基本图像属性 Python 数据分析三剑客之 Matplotlib(四):线性图的绘制 Python 数据分析三剑客之 Matplotlib(五):散点图的绘制 Python 数据分析三剑客之 Matplotlib(六):直方图 / 柱状图 / 条形图的绘制 Python 数据分析三剑客之 Matplotlib(七):饼状图的绘制 Python 数据分析三剑客之 Matplotlib(八):等高线 / 等值线图的绘制 Python 数据分析三剑客之 Matplotlib(九):极区图 / 极坐标图 / 雷达图的绘制 Python 数据分析三剑客之 Matplotlib(十):3D 图的绘制 Python 数据分析三剑客之 Matplotlib(十一):最热门最常用的 50 个图表【译文】 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106066852未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【1x00】等高线概念参考百度百科,等高线概念总结如下:等高线指的是地形图上高程相等的相邻各点所连成的闭合曲线。把地面上海拔高度相同的点连成的闭合曲线,并垂直投影到一个水平面上,并按比例缩绘在图纸上,就得到等高线。等高线也可以看作是不同海拔高度的水平面与实际地面的交线,所以等高线是闭合曲线。在等高线上标注的数字为该等高线的海拔。 位于同一等高线上的地面点,海拔高度相同。但海拔高度相同的点不一定位于同一条等高线上; 在同一幅图内,除了陡崖以外,不同高程的等高线不能相交; 在图廓内相邻等高线的高差一般是相同的,因此地面坡度与等高线之间的等高线平距成反比,等高线平距愈小,等高线排列越密,说明地面坡度越大;等高线平距愈大,等高线排列越稀,则说明地面坡度愈小; 等高线是一条闭合的曲线,如果不能在同一幅内闭合,则必在相邻或者其他图幅内闭合。 等高线经过山脊或山谷时改变方向,因此,山脊线或者山谷线应垂直于等高线转折点处的切线,即等高线与山脊线或者山谷线正交。 在 Matplotlib 等高线的绘制中,需要传递三个基本参数:某个点的 x、y 轴坐标以及其高度。 【2x00】理解 numpy.meshgrid()numpy.meshgrid() 方法用于生成网格点坐标矩阵。 123456import numpy as npa = np.array([1, 2, 3])b = np.array([7, 8, 9])res = np.meshgrid(a, b)print(res) 输出结果: 123456[array([[1, 2, 3], [1, 2, 3], [1, 2, 3]]), array([[7, 7, 7], [8, 8, 8], [9, 9, 9]])] 给定两个数组,a[1, 2, 3] 和 b[7, 8, 9],a 作为 x 轴数据,b 作为 y 轴数据,那么一共可以绘制出 9 个点: (1,7)、(1,8)、(1,9)、(2,7)、(2,8)、(2,9)、(3,7)、(3,8)、(3,9),而 numpy.meshgrid() 方法就是起这样的作用,返回的两个二维数组,横坐标矩阵 a 中的每个元素,与纵坐标矩阵 b 中对应位置元素,共同构成一个点的完整坐标。 因为在 matplotlib.pyplot.contour() 等高线绘制函数中接收的是二维坐标信息,所以在绘制等高线图之前要将原数据经过 numpy.meshgrid() 方法处理,也可以自己构建类似于上述的二维数组。 【3x00】绘制方法 matplotlib.pyplot.contour()matplotlib.pyplot.contour() 方法可用于绘制等高线图。 基本语法:matplotlib.pyplot.contour(\\*args, data=None, \\*\\*kwargs) 通用格式:matplotlib.pyplot.contour([X, Y,] Z, [levels], **kwargs) 基本参数: 参数 描述 X, Y 数组形式的点的 x 和 y 轴坐标,两者都必须是二维的,形状与 Z 相同 Z 绘制轮廓的高度值,二维数组,每个元素是其对应点的高度 levels 确定等高线的数目和位置,如果是整数 N,则使用 N 个数据间隔,即绘制 N+1 条等高线如果是数组形式,则绘制指定的等高线。值必须按递增顺序排列 其他参数: 参数 描述 colors 等高线的颜色,颜色字符串或颜色序列 cmap 等高线的颜色,字符串或者 Colormap通常包含一系列的渐变色或其他颜色组合,取值参见【6x00】Colormap 取值 alpha 透明度,介于0(透明)和1(不透明)之间 origin 通过指定 Z[0,0] 的位置来确定 Z 的方向和确切位置,仅当未指定 X, Y 时才有意义None:Z[0,0] 位于左下角的 X=0, Y=0 处'lower':Z [0, 0] 位于左下角的 X = 0.5, Y = 0.5 处'upper':Z[0,0] 位于左上角的 X=N+0.5, Y=0.5 处'image':使用 rcParams[“image.origin”] = 'upper'的值 antialiased 是否启用抗锯齿渲染,默认 True linewidths 等高线的线宽,如果是数字,则所有等高线都将使用此线宽如果是序列,则将按指定的顺序以升序打印线宽默认为 rcParams[“lines.linewidth”] = 1.5 linestyles 等高线的样式,如果线条颜色为单色,则负等高线默认为虚线'-' or 'solid', '--' or 'dashed', '-.' or 'dashdot' ':' or 'dotted', 'none' or ' ' or '' 【4x00】填充方法 matplotlib.pyplot.contourf()matplotlib.pyplot.contourf() 方法与 matplotlib.pyplot.contour() 的区别在于:contourf() 会对等高线间的区域进行颜色填充(filled contours)。除此之外两者的函数签名和返回值都相同。 基本语法:matplotlib.pyplot.contourf(\\*args, data=None, \\*\\*kwargs) 通用格式:matplotlib.pyplot.contour([X, Y,] Z, [levels], **kwargs) 基本参数: 参数 描述 X, Y 数组形式的点的 x 和 y 轴坐标,两者都必须是二维的,形状与 Z 相同 Z 绘制轮廓的高度值,二维数组,每个元素是其对应点的高度 levels 确定等高线的数目和位置,如果是整数 N,则使用 N 个数据间隔,即绘制 N+1 条等高线如果是数组形式,则绘制指定的等高线。值必须按递增顺序排列 其他参数: 参数 描述 colors 等高线的填充颜色,颜色字符串或颜色序列 cmap 等高线的填充颜色,字符串或者 Colormap通常包含一系列的渐变色或其他颜色组合,取值参见【6x00】Colormap 取值 alpha 透明度,介于0(透明)和1(不透明)之间 origin 通过指定 Z[0,0] 的位置来确定 Z 的方向和确切位置,仅当未指定 X, Y 时才有意义None:Z[0,0] 位于左下角的 X=0, Y=0 处'lower':Z [0, 0] 位于左下角的 X = 0.5, Y = 0.5 处'upper':Z[0,0] 位于左上角的 X=N+0.5, Y=0.5 处'image':使用 rcParams[“image.origin”] = 'upper'的值 antialiased 是否启用抗锯齿渲染,默认 True linewidths 等高线的线宽,如果是数字,则所有等高线都将使用此线宽如果是序列,则将按指定的顺序以升序打印线宽默认为 rcParams[“lines.linewidth”] = 1.5 linestyles 等高线的样式,如果线条颜色为单色,则负等高线默认为虚线'-' or 'solid', '--' or 'dashed', '-.' or 'dashdot' ':' or 'dotted', 'none' or ' ' or '' 【5x00】标记方法 matplotlib.pyplot.clabel()matplotlib.pyplot.clabel(CS, \\*args, \\*\\*kwargs) 方法可用于标记等高线图。 参数 描述 CS ContourSet(等高线集)对象,即 pyplot.contour() 返回的对象 levels 需要标记的等高线集,数组类型,如果未指定则默认标记所有等高线 fontsize 标记的字体大小,可选项:'xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large' colors 标记的颜色,颜色字符串或颜色序列 inline 是否在标签位置移除轮廓显示,bool 类型,默认 True inline_spacing 标签位置移除轮廓的宽度,float 类型,默认为 5 fmt 标签的格式字符串。str 或 dict 类型,默认值为 %1.3f rightside_up 是否将标签旋转始终与水平面成正负90度,bool 类型,默认 True use_clabeltext 默认为 False,如果为 True,则使用 ClabelText 类(而不是 Text)创建标签ClabelText 在绘图期间重新计算文本的旋转角度,如果轴的角度发生变化,则可以使用此功能 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106066852未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【6x00】Colormap 取值matplotlib.pyplot.contour() 和 matplotlib.pyplot.contourf() 中 cmap 参数用于设置等高线的颜色,取值通常为 Colormap 中的值,通常包含一系列的渐变色或其他颜色组合。具体参加下图。 官方文档:https://matplotlib.org/tutorials/colors/colormaps.html 【7x00】简单示例12345678910111213141516171819202122import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = np.arange(-2.0, 2.0, 0.01)y = np.arange(-2.0, 2.0, 0.01)m, n = np.meshgrid(x, y) # 生成网格点坐标矩阵# 指定一个函数用于计算每个点的高度,也可以直接使用二维数组储存每个点的高度def f(a, b): return (1 - b ** 5 + a ** 5) * np.exp(-a ** 2 - b ** 2)# 绘制等高线图,8 个数据间隔,颜色为黑色plt.contour(m, n, f(m, n), 8, colors='k')plt.title('等高线图简单示例')plt.xlabel('x axis label')plt.ylabel('y axis label')plt.show() 【8x00】添加标记matplotlib.pyplot.clabel() 方法用于给等高线添加标记。 123456789101112131415161718192021222324import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = np.arange(-2.0, 2.0, 0.01)y = np.arange(-2.0, 2.0, 0.01)m, n = np.meshgrid(x, y) # 生成网格点坐标矩阵# 指定一个函数用于计算每个点的高度,也可以直接使用二维数组储存每个点的高度def f(a, b): return (1 - b ** 5 + a ** 5) * np.exp(-a ** 2 - b ** 2)# 绘制等高线图,8 个数据间隔,颜色为黑色C = plt.contour(m, n, f(m, n), 8, colors='k')# 添加标记,标记处不显示轮廓线,颜色为黑红绿蓝四种,保留两位小数plt.clabel(C, inline=True, colors=['k', 'r', 'g', 'b'], fmt='%1.2f')plt.title('等高线图添加标记示例')plt.xlabel('x axis label')plt.ylabel('y axis label')plt.show() 【9x00】轮廓线颜色和样式matplotlib.pyplot.contour() 方法中,colors 参数即可为等高线轮廓设置颜色,可以是单色,也可以是一个颜色列表,linestyles 参数可以设置轮廓线样式,注意,如果线条颜色为单色,则负等高线(高度值为负)默认为虚线。 12345678910111213141516171819202122232425import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = np.arange(-2.0, 2.0, 0.01)y = np.arange(-2.0, 2.0, 0.01)m, n = np.meshgrid(x, y) # 生成网格点坐标矩阵# 指定一个函数用于计算每个点的高度,也可以直接使用二维数组储存每个点的高度def f(a, b): return (1 - b ** 5 + a ** 5) * np.exp(-a ** 2 - b ** 2)colors = ['k', 'r', 'g', 'b']# 绘制等高线图,8 个数据间隔,颜色为黑色,线条样式为 --C = plt.contour(m, n, f(m, n), 8, colors=colors, linestyles='--')# 添加标记,标记处不显示轮廓线,颜色为黑红绿蓝四种,保留两位小数plt.clabel(C, inline=True, colors=colors, fmt='%1.2f')plt.title('等高线图设置颜色/样式示例')plt.xlabel('x axis label')plt.ylabel('y axis label')plt.show() 如果想启用渐变色,则可以设置 cmap,取值参见【6x00】Colormap 取值,colorbar() 方法可以显示颜色对照条。 1234567891011121314151617181920212223242526import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = np.arange(-2.0, 2.0, 0.01)y = np.arange(-2.0, 2.0, 0.01)m, n = np.meshgrid(x, y) # 生成网格点坐标矩阵# 指定一个函数用于计算每个点的高度,也可以直接使用二维数组储存每个点的高度def f(a, b): return (1 - b ** 5 + a ** 5) * np.exp(-a ** 2 - b ** 2)# 绘制等高线图,8 个数据间隔,颜色为 plasmaC = plt.contour(m, n, f(m, n), 8, cmap='plasma')# 添加标记,标记处不显示轮廓线,颜色为黑色,保留两位小数plt.clabel(C, inline=True, colors='k', fmt='%1.2f')# 显示颜色条plt.colorbar()plt.title('等高线图设置渐变色示例')plt.xlabel('x axis label')plt.ylabel('y axis label')plt.show() 【10x00】颜色填充matplotlib.pyplot.contourf() 方法用于对等高线之间的地方进行颜色填充。 123456789101112131415161718192021222324252627import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = np.arange(-2.0, 2.0, 0.01)y = np.arange(-2.0, 2.0, 0.01)m, n = np.meshgrid(x, y) # 生成网格点坐标矩阵# 指定一个函数用于计算每个点的高度,也可以直接使用二维数组储存每个点的高度def f(a, b): return (1 - b ** 5 + a ** 5) * np.exp(-a ** 2 - b ** 2)# 绘制等高线图,8 个数据间隔,颜色为 plasmaplt.contourf(m, n, f(m, n), 8, cmap='plasma')C = plt.contour(m, n, f(m, n), 8, cmap='plasma')# 添加标记,标记处不显示轮廓线,颜色为黑色,保留两位小数plt.clabel(C, inline=True, colors='k', fmt='%1.2f')# 显示颜色条plt.colorbar()plt.title('等高线图颜色填充示例')plt.xlabel('x axis label')plt.ylabel('y axis label')plt.show() 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106066852未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Matplotlib","slug":"Python-数据分析/Matplotlib","permalink":"https://www.itrhx.com/categories/Python-数据分析/Matplotlib/"}],"tags":[{"name":"Matplotlib","slug":"Matplotlib","permalink":"https://www.itrhx.com/tags/Matplotlib/"},{"name":"等高线图","slug":"等高线图","permalink":"https://www.itrhx.com/tags/等高线图/"},{"name":"等值线图","slug":"等值线图","permalink":"https://www.itrhx.com/tags/等值线图/"}]},{"title":"Python 数据分析三剑客之 Matplotlib(七):饼状图的绘制","slug":"A74-Matplotlib-07","date":"2020-04-23T17:14:19.796Z","updated":"2020-08-06T03:18:07.852Z","comments":true,"path":"2020/04/24/A74-Matplotlib-07/","link":"","permalink":"https://www.itrhx.com/2020/04/24/A74-Matplotlib-07/","excerpt":"","text":"Matplotlib 系列文章: Python 数据分析三剑客之 Matplotlib(一):初识 Matplotlib 与其 matplotibrc 配置文件 Python 数据分析三剑客之 Matplotlib(二):文本描述 / 中文支持 / 画布 / 网格等基本图像属性 Python 数据分析三剑客之 Matplotlib(三):图例 / LaTeX / 刻度 / 子图 / 补丁等基本图像属性 Python 数据分析三剑客之 Matplotlib(四):线性图的绘制 Python 数据分析三剑客之 Matplotlib(五):散点图的绘制 Python 数据分析三剑客之 Matplotlib(六):直方图 / 柱状图 / 条形图的绘制 Python 数据分析三剑客之 Matplotlib(七):饼状图的绘制 Python 数据分析三剑客之 Matplotlib(八):等高线 / 等值线图的绘制 Python 数据分析三剑客之 Matplotlib(九):极区图 / 极坐标图 / 雷达图的绘制 Python 数据分析三剑客之 Matplotlib(十):3D 图的绘制 Python 数据分析三剑客之 Matplotlib(十一):最热门最常用的 50 个图表【译文】 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106025845未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【1x00】方法描述matplotlib.pyplot.pie() 方法用于绘制饼状图。 基本语法: 1234567matplotlib.pyplot.pie( x[, explode=None, labels=None, colors=None, autopct=None, pctdistance=0.6, shadow=False, labeldistance=1.1, startangle=None, radius=None, counterclock=True, wedgeprops=None, textprops=None, center=(0, 0), frame=False, rotatelabels=False, \\*, data=None] ) 参数 描述 x 每个扇形块的大小,数组形式,大小单位是比例 explode 指定对应扇形块脱离饼图的半径大小,数组形式,其中元素个数应该是 len(x) labels 每个扇形块上的文本标签,列表形式 labeldistance 每个扇形块上的文本标签与扇形中心的距离,float 类型,默认 1.1 colors 每个扇形块对应的颜色,数组形式 autopct 用于计算每个扇形块所占比例,字符串或者函数类型例如:autopct='%1.1f%%' 表示浮点数,保留一位小数,并添加百分比符号 pctdistance 每个扇形块的中心与 autopct 生成的文本之间的距离,float 类型,默认 0.6 shadow 是否为扇形添加阴影效果 startangle 将饼图按照逆时针旋转指定的角度,float 类型 radius 饼图的半径,如果是 None,则将被设置为 1,float 类型 counterclock 是否按照逆时针对扇形图进行排列,bool 类型,默认 True wedgeprops 传递给绘制每个扇形图对象的参数,字典形式,参数值参见 Wedge例如:wedgeprops = {'linewidth': 3} 设置扇形边框线宽度为 3 textprops 传递给文本对象的参数,字典形式例如:textprops={'color': 'r', 'fontsize': 15} 设置文字为红色,大小为15 center 饼图圆心在画布上是坐标,默认 (0, 0) frame 是否显示 x, y 坐标轴外框,默认 False rotatelabels 是否按照角度进行调整每块饼的 label 文本标签,默认 False 【2x00】简单示例12345678910111213import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = [10, 30, 45, 15]labels = ['Java', 'Golang', 'Python', 'C++']colors = ['red', 'yellow', 'blue', 'green']# 指定4个扇区所占比例以及扇区的颜色,扇区文本标签距离扇区中心1.1plt.pie(x, labels=labels, colors=colors, labeldistance=1.1)plt.title('饼状图简单示例')plt.show() 【3x00】按角度调整扇形标签rotatelabels 属性可以设置是否按照角度调整每块饼的 label(标签)显示方式。 12345678910111213import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = [10, 30, 45, 15]labels = ['Java', 'Go', 'Python', 'C++']colors = ['red', 'yellow', 'blue', 'green']# 指定4个扇区所占比例以及扇区的颜色,扇区文本标签距离扇区中心1.1,按角度调整 labelsplt.pie(x, labels=labels, colors=colors, labeldistance=1.1, rotatelabels=True)plt.title('饼状图按角度调整 labels 示例')plt.show() 【4x00】显示图例与前面文章中绘制线性图、散点图、条形图一样,调用 matplotlib.pyplot.legend() 方法可绘制图例,该方法的参数解释参见前文《Python 数据分析三剑客之 Matplotlib(三):图例 / LaTeX / 刻度 / 子图 / 补丁等基本图像属性》 12345678910111213import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = [10, 30, 45, 15]labels = ['Java', 'Go', 'Python', 'C++']colors = ['red', 'yellow', 'blue', 'green']plt.pie(x, labels=labels, colors=colors, labeldistance=1.1)plt.title('饼状图显示图例示例')plt.legend(bbox_to_anchor=(1, 1))plt.show() 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106025845未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【5x00】突出显示扇形块explode 参数可以实现突出显示某一块扇区,接收数组形式的参数,这个数组中的元素个数应该是 len(x),即和扇区块的数量相同。 1234567891011121314import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = [10, 30, 45, 15]labels = ['Java', 'Golang', 'Python', 'C++']colors = ['red', 'yellow', 'blue', 'green']# 指定第一个扇区块脱离饼图的半径大小为0.3,其它扇区不脱离plt.pie(x, labels=labels, colors=colors, labeldistance=1.1, explode=[0.3, 0, 0, 0])plt.title('饼状图突出显示扇形块示例')plt.legend(bbox_to_anchor=(1, 1))plt.show() 【6x00】显示各扇区所占百分比autopct 参数可用于计算每个扇形块所占比例,接收字符串或者函数类型,例如:autopct='%1.1f%%' 表示浮点数,保留一位小数,并添加百分比符号。pctdistance 参数用于调整每个扇形块的中心与 autopct 生成的文本之间的距离,float 类型,默认 0.6。 123456789101112131415161718192021import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = [10, 30, 45, 15]labels = ['Java', 'Golang', 'Python', 'C++']colors = ['red', 'yellow', 'blue', 'green']plt.pie( x, # 每个扇形块所占比例 labels=labels, # 扇形块文本标签 colors=colors, # 扇形块颜色 labeldistance=1.1, # 扇形块标签距离中心的距离 explode=[0.3, 0, 0, 0], # 第一个扇形块突出显示 autopct='%1.1f%%', # 显示百分比,保留一位小数 pctdistance=0.5 # 百分比文本距离饼状图中心的距离)plt.title('饼状图显示各扇区所占百分比示例')plt.legend(bbox_to_anchor=(1, 1)) # 显示图例plt.show() 【7x00】旋转饼状图startangle 参数可以选择饼状图,改变饼状图放置的角度。注意是按照逆时针旋转。 12345678910111213141516171819202122import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = [10, 30, 45, 15]labels = ['Java', 'Golang', 'Python', 'C++']colors = ['red', 'yellow', 'blue', 'green']plt.pie( x, # 每个扇形块所占比例 labels=labels, # 扇形块文本标签 colors=colors, # 扇形块颜色 labeldistance=1.1, # 扇形块标签距离中心的距离 explode=[0.3, 0, 0, 0], # 第一个扇形块突出显示 autopct='%1.1f%%', # 显示百分比,保留一位小数 pctdistance=0.5, # 百分比文本距离饼状图中心的距离 startangle=-90 # 逆时针旋转-90°,即顺时针旋转90°)plt.title('饼状图旋转角度示例')plt.legend(bbox_to_anchor=(1, 1)) # 显示图例plt.show() 【8x00】自定义每个扇形和文字属性wedgeprops 参数以字典形式为每个扇形添加自定义属性,例如:wedgeprops = {'linewidth': 3} 设置扇形边框线宽度为 3,更多其他参数值参见 Wedge; textprops 参数同样以字典形式为文本对象添加自定义属性,例如:textprops={'color': 'r', 'fontsize': 15} 设置文字为红色,大小为15,更多其他参数值参见 Text。 1234567891011121314151617181920212223242526272829303132import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = [10, 30, 45, 15]labels = ['Java', 'Golang', 'Python', 'C++']colors = ['red', 'yellow', 'blue', 'green']plt.pie( x, # 每个扇形块所占比例 labels=labels, # 扇形块文本标签 colors=colors, # 扇形块颜色 labeldistance=1.1, # 扇形块标签距离中心的距离 explode=[0.3, 0, 0, 0], # 第一个扇形块突出显示 autopct='%1.1f%%', # 显示百分比,保留一位小数 pctdistance=0.6, # 百分比文本距离饼状图中心的距离 shadow=True, # 显示阴影效果 wedgeprops={ # 为每个扇形添加属性 'width': 0.7, # 扇形宽度0.7 'edgecolor': '#98F5FF', # 扇形边缘线颜色 'linewidth': 3 # 扇形边缘线宽度 }, textprops={ # 为文字添加属性 'fontsize': 13, # 文字大小 'fontweight': 'bold', # 文字粗细 'color': 'k' # 文字颜色,黑色 })plt.title('饼状图自定义每个扇形和文字属性示例', fontweight='bold')plt.legend(bbox_to_anchor=(1, 1), borderpad=0.6) # 显示图例plt.show() 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106025845未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Matplotlib","slug":"Python-数据分析/Matplotlib","permalink":"https://www.itrhx.com/categories/Python-数据分析/Matplotlib/"}],"tags":[{"name":"Matplotlib","slug":"Matplotlib","permalink":"https://www.itrhx.com/tags/Matplotlib/"},{"name":"饼状图","slug":"饼状图","permalink":"https://www.itrhx.com/tags/饼状图/"}]},{"title":"Python 数据分析三剑客之 Matplotlib(六):直方图/柱状图/条形图的绘制","slug":"A73-Matplotlib-06","date":"2020-04-21T15:29:46.364Z","updated":"2020-08-06T03:16:13.213Z","comments":true,"path":"2020/04/21/A73-Matplotlib-06/","link":"","permalink":"https://www.itrhx.com/2020/04/21/A73-Matplotlib-06/","excerpt":"","text":"Matplotlib 系列文章: Python 数据分析三剑客之 Matplotlib(一):初识 Matplotlib 与其 matplotibrc 配置文件 Python 数据分析三剑客之 Matplotlib(二):文本描述 / 中文支持 / 画布 / 网格等基本图像属性 Python 数据分析三剑客之 Matplotlib(三):图例 / LaTeX / 刻度 / 子图 / 补丁等基本图像属性 Python 数据分析三剑客之 Matplotlib(四):线性图的绘制 Python 数据分析三剑客之 Matplotlib(五):散点图的绘制 Python 数据分析三剑客之 Matplotlib(六):直方图 / 柱状图 / 条形图的绘制 Python 数据分析三剑客之 Matplotlib(七):饼状图的绘制 Python 数据分析三剑客之 Matplotlib(八):等高线 / 等值线图的绘制 Python 数据分析三剑客之 Matplotlib(九):极区图 / 极坐标图 / 雷达图的绘制 Python 数据分析三剑客之 Matplotlib(十):3D 图的绘制 Python 数据分析三剑客之 Matplotlib(十一):最热门最常用的 50 个图表【译文】 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105952856未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【1x00】直方图 / 柱状图 / 条形图的区别 直方图:直方图(Histogram)又称质量分布图,是一种统计报告图,由一系列高度不等的纵向条纹或线段表示数据分布的情况。一般用于描述连续型数据的分布关系,用横轴表示数据类型,纵轴表示分布情况。直方图是用面积表示各组频数的多少,矩形的高度表示每一组的频数或频率,宽度则表示各组的组距,因此其高度与宽度均有意义。其次,由于分组数据具有连续性,直方图的各矩形通常是连续排列。 柱状图:柱状图(bar chart)又称条图、长条图、柱状统计图、条状图、棒形图,是一种以长方形的长度为变量的统计图表。一般用于描述离散型分类数据的对比,长条图用来比较两个或以上的价值(不同时间或者不同条件),只有一个变量,通常利用于较小的数据集分析。柱状图亦可横向排列,或用多维方式表达。柱状图各矩形的宽度固定,矩形之间分开排列,会有间距。 条形图:通常情况下条形图 = 柱状图,也可以将横向排列的柱状图称为条形图。在本文中会将条形图视为后者。 【2x00】直方图的绘制【2x01】函数介绍 matplotlib.pyplot.hist()matplotlib.pyplot.hist() 函数用于绘制直方图。 基本语法:matplotlib.pyplot.hist(x[, bins=None, range=None, density=False, bottom=None, histtype='bar', align='mid', orientation='vertical', rwidth=None, log=False, color=None, label=None, stacked=False, \\*\\*kwargs]) 基本参数: 参数 描述 x 数据集,数组或数组序列 bins 统计的分布区间、条形数,可以是整数、序列或字符串,默认 rcParams["hist.bins"] =10如果 bins 是整数,则定义的是等宽的矩形的个数如果 bins 是序列,则定义的是每个矩形的区间,如:bins = [1, 2, 3, 4],则矩形分布区间为 [1,2)、[2,3)、[3,4]如果 bins 是字符串,则它应该是 numpy.histogram_bin_edges 所支持的策略之一 range 矩形分布的区间,在没有指定 bins 生效,元组类型 density 是否显示频率统计结果,频率统计结果=区间数目/(总数*区间宽度) bottom y 轴的起始位置,默认为 0 histtype 矩形的样式,有四种类型可选:'bar':默认值,传统的条形直方图,如果给出多个数据,则条形图并排排列'barstacked':当数据为 1 个时,和 bar 结果一样,当数据为多个时,则进行垂直堆叠'step':未填充的线条形式;'stepfilled':填充的线条形式,效果与 bar 差不多 align 矩形的中心位于 bins(x 轴) 的位置,'left':左;'mid':中;'right':右 orientation 矩形的方向,vertical:垂直;horizontal:水平 rwidth 矩形的相对宽度,如果未指定,则自动计算宽度 log y 坐标轴是否以指数刻度显示 color 矩形的颜色,默认蓝色,与 facecolor 作用相同,指定一个即可,如果两者都指定,则取 facecolor 的值 label 数据的标签,展示图例时使用 stacked 是否为堆积状图(当两个数据相似时,堆积在一起就会把第一个数据的显示相对缩小一点) 其他参数: 参数 描述 facecolor 标量或数组类型,每个矩形的颜色,与 color 作用相同,指定一个即可,如果两者都指定,则取 facecolor 的值 edgecolor 标量或数组类型,直方图边缘线的颜色 linewidth 标量或数组类型,直方图边缘线的宽度,如果为 0,则不绘制边 alpha float 类型,矩形透明度 label 图例中显示的标签 linestyle / ls 线条样式,此处指矩形边缘线条样式'-' or 'solid', '--' or 'dashed', '-.' or 'dashdot' or ':' or 'dotted', 'none' or ' ' or '' linewidth / lw 线条宽度,此处指矩形边缘线的宽度,float 类型,默认 0.8 hatch 矩形的填充图案,可以是组合形式,如果有相同的图案,则会增加填充的密度取值可以是:'/', '\\', `’ ‘,‘-‘,‘+’,‘x’,‘o’,‘O’,‘.’,‘*’` 【2x02】简单直方图示例123456789101112131415import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei'] # 设置中文显示x = np.random.randint(0, 101, 100) # 数据集bins = np.arange(0, 101, 10) # 分布区间 [0,10)、[10,20)...[90,100]plt.hist(x, bins=bins, linewidth=0.5, edgecolor='k') # 边缘线宽0.5,颜色为黑色plt.xlim(0, 100) # x 轴刻度范围 plt.title('简单直方图示例') # 标题plt.xlabel('x axis label') # x 轴标签plt.ylabel('y axis label') # y 轴标签plt.show() 【2x03】堆积的直方图参数 stacked 决定了将两份数据进行堆积显示。注意,有可能两个数据相似(y 轴的值相似),但是堆积在一起的时候,会把第一个数据的显示相对缩小一点。 1234567891011121314151617181920import matplotlib.pyplot as pltimport numpy as npplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']hist1 = np.random.randint(0, 100, 100)hist2 = np.random.randint(0, 100, 100)x = [hist1, hist2]colors = ['orchid', 'deepskyblue']labels = ['hist1', 'hist2']bins = range(0, 101, 10)# 绘制两份数据的直方图,数据集等其他参数可以使用列表形式传递,也可以使用两次 hist 函数单独传递plt.hist(x, bins=bins, color=colors, stacked=True, label=labels)plt.title('堆积的直方图示例')plt.xlabel('x axis label')plt.ylabel('y axis label')plt.legend(loc=\"upper left\")plt.show() 【2x04】填充其他样式hatch 参数可以让直方图的矩形填充其他样式,可选值有:'/', '\\', '|', '-', '+', 'x', 'o', 'O', '.', '*'。可以是不同图案的组合形式,如果有相同的图案,则会增加填充的密度。 12345678910111213141516import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei'] # 设置中文显示x = np.random.randint(0, 101, 100) # 数据集bins = np.arange(0, 101, 10) # 分布区间 [0,10)、[10,20)...[90,100]# 矩形颜色为白色,使用 / 填充,边缘线宽0.5,颜色为黑色plt.hist(x, bins=bins, color='w', hatch='///', linewidth=0.5, edgecolor='k')plt.xlim(0, 100) # x 轴刻度范围plt.title('直方图图案填充示例') # 标题plt.xlabel('x axis label') # x 轴标签plt.ylabel('y axis label') # y 轴标签plt.show() 【3x00】柱状图的绘制【3x01】函数介绍 matplotlib.pyplot.bar()matplotlib.pyplot.bar() 函数用于绘制柱状图。 基本语法:matplotlib.pyplot.bar(x, height[, width=0.8, bottom=None, align='center', \\*\\*kwargs]) 基本参数: 参数 描述 x 标量序列,每个矩形对应的 x 轴刻度 height 标量或标量序列,每个矩形对应的高度,即 y 轴刻度 width 标量或数组类型,每个矩形的宽度,默认为 0.8 bottom 标量或数组类型,y 轴的起始位置,默认为 0 align 矩形与 x 轴刻度对齐的位置,'center':中;'edge':左边缘 其他参数: 参数 描述 color 标量或数组类型,每个矩形的颜色,与 facecolor 作用相同,指定一个即可,如果两者都指定,则取 facecolor 的值 edgecolor 标量或数组类型,柱状图边缘线的颜色 linewidth 标量或数组类型,柱状图边缘线的宽度,如果为0,则不绘制边 tick_label 标量或数组类型,柱状图 x 轴的刻度标签,默认使用数字标签 xerr / yerr 标量,指定对应标准差(添加误差线时会用到) ecolor 标量或数组类型,误差线的线条颜色,默认值为 black capsize 标量,误差线两头横线的宽度,默认为 rcParams["errorbar.capsize"] = 0.0 error_kw 字典类型,可以此字典中定义 ecolor 和 capsize,比单独指定的优先级要高 log bool 值,y 坐标轴是否以指数刻度显示 alpha float 类型,矩形透明度 label 图例中显示的标签 linestyle / ls 线条样式,此处指矩形边缘线条样式'-' or 'solid', '--' or 'dashed', '-.' or 'dashdot' or ':' or 'dotted', 'none' or ' ' or '' linewidth / lw 线条宽度,此处指矩形边缘线的宽度,float 类型,默认 0.8 hatch 矩形的填充图案,可以是组合形式,如果有相同的图案,则会增加填充的密度取值可以是:'/', '\\', `’ ‘,‘-‘,‘+’,‘x’,‘o’,‘O’,‘.’,‘*’` 【3x02】简单柱状图示例12345678910111213141516171819import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = [1, 2, 3, 4, 5]height = [5, 7, 4, 3, 1]# 设置 x 轴的标签,也可以用 plt.xticks 方法来设置tick_label = ['A', 'B', 'C', 'D', 'E']# 设置颜色序列color = ['red', 'yellow', 'peru', 'orchid', 'deepskyblue']# 绘制柱状图,边缘线宽度为1,颜色为黑色,样式为 --plt.bar(x, height, tick_label=tick_label, color=color, edgecolor='k', linewidth=1, linestyle='--')plt.title('简单柱状图示例')plt.xlabel('x axis label')plt.ylabel('y axis label')plt.show() 【3x03】添加与标准差的误差线首先定义一个列表,其中的元素是与每个值对应的标准差,ecolor 和 capsize 参数分别指定误差线的颜色和两头横线的宽度。这两个参数可以通过 error_kw 字典形式组合起来。以字典形式的组合优先级别要比单独指定高。另外,柱状图指定标准差时要用 yerr,条形图(横向排列的柱状图)指定标准差时要用 xerr。 12345678910111213141516171819202122232425262728293031import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = [1, 2, 3, 4, 5]height = [5, 7, 4, 3, 2]std = [0.5, 0.1, 1.2, 0.3, 1.0] # 标准差tick_label = ['A', 'B', 'C', 'D', 'E'] # 设置 x 轴的标签,也可以用 plt.xticks 方法来设置color = ['red', 'yellow', 'peru', 'orchid', 'deepskyblue'] # 设置颜色序列plt.bar( x, height, tick_label=tick_label, color=color, yerr=std, # 指定对应标准差 # error_kw={ # 'ecolor': 'k', # 指定误差线的颜色 # 'capsize': 6 # 指定误差线两头横线的宽度 # }, ecolor='k', capsize=6, edgecolor='k', # 指定边缘线颜色 linewidth=1 # 指定边缘线宽度)plt.title('柱状图添加误差线示例')plt.xlabel('x axis label')plt.ylabel('y axis label')plt.show() 【3x04】多序列柱状图在绘制多序列的柱状图时,只需要多次调用 matplotlib.pyplot.bar() 函数即可,指定一个较小的宽度值(偏移量),绘制不同数据时设置不同的 x 位置刻度即可。 12345678910111213141516171819202122232425import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = np.arange(5)height1 = np.array([5, 7, 4, 3, 2])height2 = np.array([2, 4, 6, 7, 3])height3 = np.array([3, 1, 7, 5, 2])# 设置宽度值(偏移量)width = 0.3# 绘制不同数据时,x 轴依次增加一个偏移量plt.bar(x, height1, width, label='bar1')plt.bar(x + width, height2, width, label='bar2')plt.bar(x + width * 2, height3, width, label='bar3')# 设置 x 轴刻度的标签plt.xticks(x + width, ['A', 'B', 'C', 'D', 'E'])plt.title('多序列柱状图示例')plt.xlabel('x axis label')plt.ylabel('y axis label')plt.legend()plt.show() 【3x05】堆积的柱状图所谓堆积图,就是将多序列数据堆积到一个矩形上显示,在柱状图中要实现堆积图,只需要改变 bottom 参数即可,bottom 参数用于设置 y 轴基线,即柱状图的底边在 y 轴上的起始刻度,第一条数据 data1 的基线可以设置为 0,即默认值,第二条数据 data2 的基线可以设置在 data1 的上方,即 bottom=data1,第三条数据 data3 的基线可以设置在 data1 + data2 的上方,即 bottom=data1+data2,以此类推。 123456789101112131415161718192021import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = np.arange(5)height1 = np.array([5, 7, 4, 3, 2])height2 = np.array([2, 4, 6, 7, 3])height3 = np.array([3, 1, 7, 5, 2])plt.bar(x, height1, label='bar1')plt.bar(x, height2, label='bar2', bottom=height1)plt.bar(x, height3, label='bar3', bottom=(height2+height1))plt.xticks(x, ['A', 'B', 'C', 'D', 'E'])plt.title('堆积的柱状图示例')plt.xlabel('x axis label')plt.ylabel('y axis label')plt.legend()plt.show() 【3x06】填充其他样式hatch 参数可以让柱状图的矩形填充其他样式,可选值有:'/', '\\', '|', '-', '+', 'x', 'o', 'O', '.', '*'。可以是不同图案的组合形式,如果有相同的图案,则会增加填充的密度。 123456789101112131415161718192021import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = np.arange(5)height1 = np.array([5, 7, 4, 3, 2])height2 = np.array([2, 4, 6, 7, 3])height3 = np.array([3, 1, 7, 5, 2])plt.bar(x, height1, label='bar1', color='w', hatch='///')plt.bar(x, height2, label='bar2', bottom=height1, color='w', hatch='xxx')plt.bar(x, height3, label='bar3', bottom=(height2+height1), color='w', hatch='|||')plt.xticks(x, ['A', 'B', 'C', 'D', 'E'])plt.title('柱状图图案填充示例')plt.xlabel('x axis label')plt.ylabel('y axis label')plt.legend()plt.show() 【3x07】添加文字描述利用 matplotlib.pyplot.text() 方法可以在柱状图每个矩形上方添加文字描述。具体参数解释可参考前面的文章:《Python 数据分析三剑客之 Matplotlib(二):文本描述 / 中文支持 / 画布 / 网格等基本图像属性》 1234567891011121314151617181920212223242526272829303132import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = np.arange(5)height1 = np.array([5, 7, 4, 3, 2])height2 = np.array([2, 4, 6, 7, 3])height3 = np.array([3, 1, 7, 5, 2])width = 0.3# 绘制不同数据时,x 轴依次增加一个偏移量plt.bar(x, height1, width, label='bar1')plt.bar(x + width, height2, width, label='bar2')plt.bar(x + width * 2, height3, width, label='bar3')# 依次添加每条数据的标签for a, b in zip(x, height1): plt.text(a, b, b, ha='center', va='bottom')for c, d in zip(x, height2): plt.text(c + width, d, d, ha='center', va='bottom')for e, f in zip(x, height3): plt.text(e + width * 2, f, f, ha='center', va='bottom')# 设置 x 轴刻度的标签plt.xticks(x + width, ['A', 'B', 'C', 'D', 'E'])plt.title('柱状图添加文字描述示例')plt.xlabel('x axis label')plt.ylabel('y axis label')plt.legend()plt.show() 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105952856未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【4x00】条形图的绘制【4x01】函数介绍 matplotlib.pyplot.barh()matplotlib.pyplot.barh() 函数用于绘制条形图(水平排列的柱状图)。 基本语法:matplotlib.pyplot.barh(y, width[, height=0.8, left=None, align='center', color, \\*\\*kwargs]) 参数 描述 y 标量或数组类型,每个矩形对应的 y 轴刻度 width 标量或数组类型,每个矩形的宽度,即 x 轴刻度 height 标量序列,每个矩形的高度,默认 0.8 left 标量序列,每个矩形的左侧 x 坐标的起始位置,默认值为 0 align 矩形的底边与 y 轴刻度对齐的位置,'center':中;'edge':底边 其他参数: 参数 描述 color 标量或数组类型,每个矩形的颜色,与 facecolor 作用相同,指定一个即可,如果两者都指定,则取 facecolor 的值 edgecolor 标量或数组类型,条形图边缘线的颜色 linewidth 标量或数组类型,条形图边缘线的宽度,如果为0,则不绘制边 tick_label 标量或数组类型,条形图 y 轴的刻度标签,默认使用数字标签 xerr / yerr 标量,指定对应标准差(添加误差线时会用到) ecolor 标量或数组类型,误差线的线条颜色,默认值为 black capsize 标量,误差线两头横线的宽度,默认为 rcParams["errorbar.capsize"] = 0.0 error_kw 字典类型,可以此字典中定义 ecolor 和 capsize,比单独指定的优先级要高 log bool 值,y 坐标轴是否以指数刻度显示 alpha float 类型,矩形透明度 label 图例中显示的标签 linestyle / ls 线条样式,此处指矩形边缘线条样式'-' or 'solid', '--' or 'dashed', '-.' or 'dashdot' or ':' or 'dotted', 'none' or ' ' or '' linewidth / lw 线条宽度,此处指矩形边缘线的宽度,float 类型,默认 0.8 hatch 矩形的填充图案,可以是组合形式,如果有相同的图案,则会增加填充的密度取值可以是:'/', '\\', `’ ‘,‘-‘,‘+’,‘x’,‘o’,‘O’,‘.’,‘*’` 【4x02】简单条形图示例12345678910111213141516import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']y = [1, 2, 3, 4, 5]width = [5, 7, 4, 3, 1]tick_label = ['A', 'B', 'C', 'D', 'E']color = ['red', 'yellow', 'peru', 'orchid', 'deepskyblue']plt.barh(y, width, tick_label=tick_label, color=color, edgecolor='k', linewidth=1, linestyle='--')plt.title('简单条形图示例')plt.xlabel('x axis label')plt.ylabel('y axis label')plt.show() 【4x03】添加与标准差的误差线与柱状图一样,首先定义一个列表,其中的元素是与每个值对应的标准差,ecolor 和 capsize 参数分别指定误差线的颜色和两头横线的宽度。这两个参数可以通过 error_kw 字典形式组合起来。以字典形式的组合优先级别要比单独指定高。另外,柱状图指定标准差时要用 yerr,条形图(横向排列的柱状图)指定标准差时要用 xerr。 12345678910111213141516171819202122232425262728293031import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']y = [1, 2, 3, 4, 5]width = [5, 7, 4, 3, 2]std = [0.5, 0.1, 1.2, 0.3, 1.0] # 标准差tick_label = ['A', 'B', 'C', 'D', 'E'] # 设置 x 轴的标签,也可以用 plt.xticks 方法来设置color = ['red', 'yellow', 'peru', 'orchid', 'deepskyblue'] # 颜色序列plt.barh( y, width, tick_label=tick_label, color=color, xerr=std, # 指定对应标准差 # error_kw={ # 'ecolor': 'k', # 指定误差线的颜色 # 'capsize': 6 # 指定误差线两头横线的宽度 # }, ecolor='k', capsize=6, edgecolor='k', # 指定边缘线颜色 linewidth=1 # 指定边缘线宽度)plt.title('条形图添加误差线示例')plt.xlabel('x axis label')plt.ylabel('y axis label')plt.show() 【4x04】多序列条形图与多序列柱状图类似,在绘制多序列的条形图时,只需要多次调用 matplotlib.pyplot.barh() 函数即可,指定一个较小的高度值(偏移量),绘制不同数据时设置不同的 y 位置刻度即可。 12345678910111213141516171819202122232425import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']y = np.arange(5)width1 = np.array([5, 7, 4, 3, 2])width2 = np.array([2, 4, 6, 7, 3])width3 = np.array([3, 1, 7, 5, 2])# 设置高度值(偏移量)height = 0.3# 绘制不同数据时,y 轴依次增加一个偏移量plt.barh(y, width1, height, label='bar1')plt.barh(y + height, width2, height, label='bar2')plt.barh(y + height * 2, width3, height, label='bar3')# 设置 y 轴刻度的标签plt.yticks(y + height, ['A', 'B', 'C', 'D', 'E'])plt.title('多序列条形图示例')plt.xlabel('x axis label')plt.ylabel('y axis label')plt.legend()plt.show() 【4x05】堆积的条形图堆积图就是将多序列数据堆积到一个矩形上显示,和堆积的柱状图类似,在条形图中要实现堆积图,只需要改变 left 参数即可,left 参数用于设置 x 轴基线,即柱状图的底边在 x 轴上的起始刻度,第一条数据 data1 的基线可以设置为 0,即默认值,第二条数据 data2 的基线可以设置在 data1 的上方,即 left=data1,第三条数据 data3 的基线可以设置在 data1 + data2 的上方,即 left=data1+data2,以此类推。 123456789101112131415161718192021import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']y = np.arange(5)width1 = np.array([5, 7, 4, 3, 2])width2 = np.array([2, 4, 6, 7, 3])width3 = np.array([3, 1, 7, 5, 2])plt.barh(y, width1, label='bar1')plt.barh(y, width2, label='bar2', left=width1)plt.barh(y, width3, label='bar3', left=(width1+width2))plt.yticks(y, ['A', 'B', 'C', 'D', 'E'])plt.title('堆积的条形图示例')plt.xlabel('x axis label')plt.ylabel('y axis label')plt.legend()plt.show() 【4x06】填充其他样式hatch 参数可以让柱状图的矩形填充其他样式,可选值有:'/', '\\', '|', '-', '+', 'x', 'o', 'O', '.', '*'。可以是不同图案的组合形式,如果有相同的图案,则会增加填充的密度。 123456789101112131415161718192021import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']y = np.arange(5)width1 = np.array([5, 7, 4, 3, 2])width2 = np.array([2, 4, 6, 7, 3])width3 = np.array([3, 1, 7, 5, 2])plt.barh(y, width1, label='bar1', color='w', hatch='///')plt.barh(y, width2, label='bar2', left=width1, color='w', hatch='xxx')plt.barh(y, width3, label='bar3', left=(width1+width2), color='w', hatch='|||')plt.yticks(y, ['A', 'B', 'C', 'D', 'E'])plt.title('条形图图案填充示例')plt.xlabel('x axis label')plt.ylabel('y axis label')plt.legend()plt.show() 【4x07】添加文字描述利用 matplotlib.pyplot.text() 方法可以在条形图每个矩形上方添加文字描述。具体参数解释可参考前面的文章:《Python 数据分析三剑客之 Matplotlib(二):文本描述 / 中文支持 / 画布 / 网格等基本图像属性》 1234567891011121314151617181920212223242526272829303132import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']y = np.arange(5)width1 = np.array([5, 7, 4, 3, 2])width2 = np.array([2, 4, 6, 7, 3])width3 = np.array([3, 1, 7, 5, 2])height = 0.3# 绘制不同数据时,y 轴依次增加一个偏移量plt.barh(y, width1, height, label='bar1')plt.barh(y + height, width2, height, label='bar2')plt.barh(y + height * 2, width3, height, label='bar3')# 依次添加每条数据的标签for a, b in zip(width1, y): plt.text(a, b-0.05, a)for c, d in zip(width2, y): plt.text(c, d+0.20, c)for e, f in zip(width3, y): plt.text(e, f+0.50, e)# 设置 y 轴刻度的标签plt.yticks(y + height, ['A', 'B', 'C', 'D', 'E'])plt.title('条形图添加文字描述示例')plt.xlabel('x axis label')plt.ylabel('y axis label')plt.legend()plt.show() 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105952856未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Matplotlib","slug":"Python-数据分析/Matplotlib","permalink":"https://www.itrhx.com/categories/Python-数据分析/Matplotlib/"}],"tags":[{"name":"Matplotlib","slug":"Matplotlib","permalink":"https://www.itrhx.com/tags/Matplotlib/"},{"name":"直方图","slug":"直方图","permalink":"https://www.itrhx.com/tags/直方图/"},{"name":"柱状图","slug":"柱状图","permalink":"https://www.itrhx.com/tags/柱状图/"},{"name":"条形图","slug":"条形图","permalink":"https://www.itrhx.com/tags/条形图/"}]},{"title":"Python 数据分析三剑客之 Matplotlib(五):散点图的绘制","slug":"A72-Matplotlib-05","date":"2020-04-18T15:50:09.015Z","updated":"2020-08-06T03:12:26.597Z","comments":true,"path":"2020/04/18/A72-Matplotlib-05/","link":"","permalink":"https://www.itrhx.com/2020/04/18/A72-Matplotlib-05/","excerpt":"","text":"Matplotlib 系列文章: Python 数据分析三剑客之 Matplotlib(一):初识 Matplotlib 与其 matplotibrc 配置文件 Python 数据分析三剑客之 Matplotlib(二):文本描述 / 中文支持 / 画布 / 网格等基本图像属性 Python 数据分析三剑客之 Matplotlib(三):图例 / LaTeX / 刻度 / 子图 / 补丁等基本图像属性 Python 数据分析三剑客之 Matplotlib(四):线性图的绘制 Python 数据分析三剑客之 Matplotlib(五):散点图的绘制 Python 数据分析三剑客之 Matplotlib(六):直方图 / 柱状图 / 条形图的绘制 Python 数据分析三剑客之 Matplotlib(七):饼状图的绘制 Python 数据分析三剑客之 Matplotlib(八):等高线 / 等值线图的绘制 Python 数据分析三剑客之 Matplotlib(九):极区图 / 极坐标图 / 雷达图的绘制 Python 数据分析三剑客之 Matplotlib(十):3D 图的绘制 Python 数据分析三剑客之 Matplotlib(十一):最热门最常用的 50 个图表【译文】 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105914929未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【1x00】方法描述matplotlib.pyplot.scatter() 方法可用于绘制散点图。 本文用到的其他图像属性可参考前面的两篇文章: 《Python 数据分析三剑客之 Matplotlib(二):文本描述 / 中文支持 / 画布 / 网格等基本图像属性》《Python 数据分析三剑客之 Matplotlib(三):图例 / LaTeX / 刻度 / 子图等基本图像属性》 基本语法:matplotlib.pyplot.scatter(x, y, s=None, c=None, marker=None, cmap=None, alpha=None, linewidths=None, edgecolors=None, \\*\\*kwargs) 参数 描述 x,y 数据位置,标量或类似数组的形式 s 标记的大小,以磅为单位,默认 rcParams['lines.markersize'] ** 2,即 6**2=36 color / c 标记的颜色,可以是单个颜色或者一个颜色列表支持英文颜色名称及其简写、十六进制颜色码等,更多颜色示例参见官网 Color Demo marker 标记的样式,默认为 rcParams["scatter.marker"] = 'o',更多样式参见表一 cmap 将浮点数映射成颜色的颜色映射表,即一个 Colormap 实例或注册的颜色表名,仅当 c 是浮点数数组时才使用 cmap alpha 标记的透明度,float 类型,取值范围:[0, 1],默认为 1.0,即不透明 linewidths 标记边缘的线宽,默认为 rcParams["lines.linewidth"] = 1.5 edgecolors 标记边缘的颜色,可以是单个颜色或者一个颜色列表支持英文颜色名称及其简写、十六进制颜色码等,更多颜色示例参见官网 Color Demo 表一:marker 标记的样式 标记 描述 "." 点 "," 像素点 "o" 圆圈 "v" 倒三角 "^" 正三角 "<" 左三角 ">" 右三角 "1" 倒三叉星 "2" 正三叉星(类似奔驰车标形状) "3" 左三叉星 "4" 右三叉星 "8" 八边形 "s" 正方形 "p" 五边形 "P" 填充的加号(粗加号) "+" 加号 "*" 星形 "h" 六边形(底部是角) "H" 六边形(底部是边) "x" x 号 "X" 填充的 x 号(粗 x 号) "D" 粗菱形(对角线相等) "d" 细菱形(对角线不等) `” “` 垂直线 "_" 水平线 0 水平线靠左 1 水平线靠右 2 垂直线靠上 3 垂直线靠下 4 左三角(比 "<" 更细) 5 右三角(比 ">" 更细) 6 正三角(比 "^" 更细) 7 倒三角(比 "v" 更细) 8 左三角(比 "<" 更细,靠左显示) 9 右三角(比 ">" 更细,靠右显示) 10 正三角(比 "^" 更细,靠上显示) 11 倒三角(比 "v" 更细,靠下显示) "None" / " " / "" 无样式 '$...$' 支持 LaTeX 数学公式,表达式用美元符号包围起来 【2x00】简单示例12345678import numpy as npimport matplotlib.pyplot as pltx = np.arange(0, 10, 1)y = np.array([3, 8, 1, 5, 7, 2, 3, 4, 5, 7])plt.scatter(x, y)plt.show() 【3x00】多条数据绘制多条数据,设置不同数据,然后多次调用 plt.scatter() 函数即可,不同数据的线条颜色会不同,系统随机,可单独指定不同颜色。 123456789101112131415161718import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = np.arange(-2*np.pi, 2*np.pi, 1)y1 = np.sin(3*x)/xy2 = np.sin(2*x)/xy3 = np.sin(1*x)/xplt.title('多数据散点图示例')plt.xlabel('x 轴')plt.ylabel('y 轴')plt.scatter(x, y1)plt.scatter(x, y2)plt.scatter(x, y3)plt.show() 【4x00】设置颜色 / 样式 / 图例1234567891011121314151617181920import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = np.arange(-2*np.pi, 2*np.pi, 1)y1 = np.sin(3*x)/xy2 = np.sin(2*x)/xy3 = np.sin(1*x)/xplt.title('散点图自定义样式示例')plt.xlabel('x 轴')plt.ylabel('y 轴')plt.scatter(x, y1, color='g', s=30, label='(x, y1)') # 默认绿色样式plt.scatter(x, y2, color='r', s=40, marker='d', label='(x, y2)') # 红色菱形plt.scatter(x, y3, color='b', s=50, marker='2', label='(x, y3)') # 蓝色正三叉星plt.legend(framealpha=0) # 显示图例,设置为全透明plt.show() 【5x00】指定位置显示文本注释matplotlib.pyplot.annotate() 方法可以在指定位置显示文本注释,参数解释常见前文:《Python 数据分析三剑客之 Matplotlib(二):文本描述 / 中文支持 / 画布 / 网格等基本图像属性》 应用举例: 1234567891011121314151617181920212223import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = [0.13, 0.22, 0.39, 0.59, 0.68, 0.74, 0.93]y = [0.75, 0.34, 0.44, 0.52, 0.80, 0.25, 0.55]plt.title('散点图添加文本注释示例')plt.xlabel('x 轴')plt.ylabel('y 轴')plt.xlim([0, 1]) # 设置 x 轴刻度的范围plt.ylim([0, 1]) # 设置 y 轴刻度的范围plt.scatter(x, y, marker='o', s=50)for m, n in zip(x, y): plt.annotate('(%s,%s)' % (m, n), xy=(m, n), xytext=(0, -10), textcoords='offset points', ha='center', # 点在注释文本的中心 va='top') # 点在注释文本的上方plt.show() 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105914929未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【6x00】随机数据散点图随机数据可以用 numpy 的 random 模块来实现。 numpy.random.rand(d0, d1, …, dn):根据给定维度生成 [0,1) 之间的数据。 numpy.random.randn(d0, d1, …, dn) :返回一个或一组具有标准正态分布的样本。 numpy.random.randint(low, high, size):返回随机整数,范围区间为 [low,high),size 为数组维度大小 应用举例: 123456789101112131415import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']N = 1000x = np.random.randn(N)y = np.random.randn(N)plt.title('散点图随机数据示例')plt.xlabel('x 轴')plt.ylabel('y 轴')plt.scatter(x, y, alpha=0.5)plt.show() 【7x00】随机颜色与色条12345678910111213141516171819import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']N = 1000x = np.random.randn(N)y = np.random.randn(N)color = np.random.rand(N)size = np.random.rand(N) * 1000plt.figure(figsize=(8.4, 5.8)) # 设置画布大小 840x580plt.title('散点图随机大小颜色示例')plt.xlabel('x 轴')plt.ylabel('y 轴')plt.scatter(x, y, c=color, s=size, alpha=0.5)plt.show() 可以用 pyplot.colorbar() 方法绘制颜色对照条。 基本语法:matplotlib.pyplot.colorbar([mappable=None, cax=None, ax=None, **kw]) 部分参数解释如下表,其他参数,如长度,宽度等请参考官方文档。 参数 描述 mappable 要设置色条的图像对象,该参数对于 Figure.colorbar 方法是必需的,但对于 pyplot.colorbar 函数是可选的 cax 可选项,要绘制色条的轴 ax 可选项,设置色条的显示位置,通常在一个画布上有多个子图时使用 **kw 可选项,其他关键字参数,参考官方文档 1234567891011121314151617181920import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']N = 1000x = np.random.randn(N)y = np.random.randn(N)color = np.random.rand(N)size = np.random.rand(N) * 1000plt.figure(figsize=(8.4, 5.8))plt.title('散点图颜色对照条示例')plt.xlabel('x 轴')plt.ylabel('y 轴')plt.scatter(x, y, c=color, s=size, alpha=0.5)plt.colorbar()plt.show() 【8x00】不同图像之间的层级调整zorder 参数用于设置不同图像之间的层级关系,数字越大,所处的层级越大,即显示越靠上。 未设置 zorder 参数前: 12345678910111213141516171819import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x1 = np.arange(-2*np.pi, 2*np.pi, 0.01)y1 = np.sin(3*x1)/x1x2 = np.arange(-2*np.pi, 2*np.pi, 1)y2 = np.sin(3*x2)/x2plt.title('不同图像之间层级调整示例')plt.xlabel('x 轴')plt.ylabel('y 轴')plt.plot(x1, y1, c='b', linewidth=3.5, label='线性图')plt.scatter(x2, y2, c='r', s=40, label='散点图')plt.legend()plt.show() 设置 zorder 参数后: 12345678910111213141516171819import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x1 = np.arange(-2*np.pi, 2*np.pi, 0.01)y1 = np.sin(3*x1)/x1x2 = np.arange(-2*np.pi, 2*np.pi, 1)y2 = np.sin(3*x2)/x2plt.title('不同图像之间层级调整示例')plt.xlabel('x 轴')plt.ylabel('y 轴')plt.plot(x1, y1, zorder=1, c='b', linewidth=3.5, label='线性图')plt.scatter(x2, y2, zorder=2, c='r', s=40, label='散点图')plt.legend()plt.show() 【9x00】框选部分数据有时候我们希望能够框选一部分数据来强调其重要性,matplotlib.patches.Polygon() 方法的作用是生成不规则的多边形补丁,matplotlib.patches 还有另外的方法可以生成矩形、圆形等其他图形,具体参见前面的文章《Python 数据分析三剑客之 Matplotlib(三):图例 / LaTeX / 刻度 / 子图 / 补丁等基本图像属性》,生成补丁之后,通过 axes.add_patch() 方法将其添加到绘图区(axes)即可。 1234567891011121314151617181920212223242526272829303132import numpy as npimport matplotlib.pyplot as pltimport matplotlib.patches as mpathesplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']plt.figure(figsize=(8.4, 5.8))x1 = np.arange(0, 1000, 10)y1 = np.random.randint(0, 1000, 100)x2 = np.arange(0, 500, 10)y2 = np.random.randint(200, 800, 50)x3 = np.random.randint(50, 800, 80)y3 = np.random.randint(50, 800, 80)x4 = np.array([0, 100, 300, 400, 350, 500, 450, 367, 420, 490])y4 = np.array([267, 800, 453, 500, 600, 420, 380, 503, 390, 600])plt.title('散点图数据框选示例', fontsize=15)plt.xlabel('x 轴', fontsize=15)plt.ylabel('y 轴', fontsize=15)plt.scatter(x1, y1, c='r', s=50, alpha=0.7, label='RED')plt.scatter(x2, y2, c='b', s=100, alpha=0.7, label='BLUE')plt.scatter(x3, y3, c='g', s=150, alpha=0.7, label='GREEN')plt.scatter(x4, y4, c='y', s=250, alpha=0.7, label='YELLOW')plt.legend(loc='upper right', borderpad=1, edgecolor='k', framealpha=1, labelspacing=1)Polygon_point = [[100, 800], [0, 267], [500, 420], [490, 600]] # 多边形补丁的顶点坐标polygon = mpathes.Polygon(Polygon_point, color='#FF69B4', alpha=0.3) # 绘制补丁,框选部分数据ax = plt.gca() # 获取当前绘图区(gca = Get Current Axes)ax.add_patch(polygon) # 将补丁添加到当前绘图区plt.show() 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105914929未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Matplotlib","slug":"Python-数据分析/Matplotlib","permalink":"https://www.itrhx.com/categories/Python-数据分析/Matplotlib/"}],"tags":[{"name":"Matplotlib","slug":"Matplotlib","permalink":"https://www.itrhx.com/tags/Matplotlib/"},{"name":"散点图","slug":"散点图","permalink":"https://www.itrhx.com/tags/散点图/"}]},{"title":"Python 数据分析三剑客之 Matplotlib(四):线性图的绘制","slug":"A71-Matplotlib-04","date":"2020-04-16T14:42:30.239Z","updated":"2020-08-06T03:10:59.357Z","comments":true,"path":"2020/04/16/A71-Matplotlib-04/","link":"","permalink":"https://www.itrhx.com/2020/04/16/A71-Matplotlib-04/","excerpt":"","text":"Matplotlib 系列文章: Python 数据分析三剑客之 Matplotlib(一):初识 Matplotlib 与其 matplotibrc 配置文件 Python 数据分析三剑客之 Matplotlib(二):文本描述 / 中文支持 / 画布 / 网格等基本图像属性 Python 数据分析三剑客之 Matplotlib(三):图例 / LaTeX / 刻度 / 子图 / 补丁等基本图像属性 Python 数据分析三剑客之 Matplotlib(四):线性图的绘制 Python 数据分析三剑客之 Matplotlib(五):散点图的绘制 Python 数据分析三剑客之 Matplotlib(六):直方图 / 柱状图 / 条形图的绘制 Python 数据分析三剑客之 Matplotlib(七):饼状图的绘制 Python 数据分析三剑客之 Matplotlib(八):等高线 / 等值线图的绘制 Python 数据分析三剑客之 Matplotlib(九):极区图 / 极坐标图 / 雷达图的绘制 Python 数据分析三剑客之 Matplotlib(十):3D 图的绘制 Python 数据分析三剑客之 Matplotlib(十一):最热门最常用的 50 个图表【译文】 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105839855未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【1x00】方法描述matplotlib.pyplot.plot() 函数可以用于绘制线性图。 本文用到的其他图像属性可参考前面的两篇文章: 《Python 数据分析三剑客之 Matplotlib(二):文本描述 / 中文支持 / 画布 / 网格等基本图像属性》《Python 数据分析三剑客之 Matplotlib(三):图例 / LaTeX / 刻度 / 子图等基本图像属性》 基本语法:matplotlib.pyplot.plot(x, y[, fmt, \\*\\*kwargs]) 参数 描述 x x 轴数据,数组类型或者标量,x 值是可选的,默认为 range(len(y)),通常为一维数组 y y 轴数据,数组类型或者标量,通常为一维数组 fmt str 类型,格式字符串,由标记、线条和颜色部分组成fmt = '[marker][line][color]',例如 ro 表示红色圆圈,三个参数的取值见后表 **kwargs 可选项,其他 Line2D 属性,常用属性见下表 部分常见 Line2D 属性如下表,完整属性参见官方文档。 属性 描述 alpha 线条透明度,float 类型,取值范围:[0, 1],默认为 1.0,即不透明 antialiased / aa 是否使用抗锯齿渲染,默认为 True color / c 线条颜色,支持英文颜色名称及其简写、十六进制颜色码等,更多颜色示例参见官网 Color Demo linestyle / ls 线条样式:'-' or 'solid', '--' or 'dashed', '-.' or 'dashdot' ':' or 'dotted', 'none' or ' ' or '' linewidth / lw 线条宽度,float 类型,默认 0.8 markeredgecolor / mec marker 标记的边缘颜色 markeredgewidth / mew marker 标记的边缘宽度 markerfacecolor / mfc marker 标记的颜色 markerfacecoloralt / mfcalt marker 标记的备用颜色 markersize / ms marker 标记的大小 fmt 中 marker、line、color 三个参数的取值: marker:线条标记样式(线条上每个数据点的样式) 字符 描述 '.' 点标记(point marker) ',' 像素点标记(pixel marker) 'o' 圆圈标记(circle marker) 'v' 下三角标记(triangle_down marker) '^' 上三角标记(triangle_up marker) '<' 左三角标记(triangle_left marker) '>' 右三角标记(triangle_right marker) '1' 下三叉星标记(tri_down marker) '2' 上三叉星标记(tri_up marker) '3' 左三叉星标记(tri_left marker) '4' 右三叉星标记(tri_right marker) 's' 正方形标记(square marker) 'p' 五角形标记(pentagon marker) '*' 星号标记(star marker) 'h' 六边形标记(hexagon1 marker) 'H' 六边形标记(hexagon2 marker) '+' 加号标记(plus marker) 'x' X 号标记(x marker) 'D' 菱形标记(diamond marker) 'd' 细菱形标记(thin_diamond marker) `’ ‘` 垂直线标记(vline marker) '_' 水平线标记(hline marker) line:线条样式 字符 描述 '-' 实线样式(solid line style) '--' 虚线样式(dashed line style) '-.' 点划线样式(dash-dot line style) ':' 点样式(dotted line style) color:线条颜色,支持英文颜色名称及其简写、十六进制颜色码等 字符 描述 'b' 蓝色(blue) 'g' 绿色(green) 'r' 红色(red) 'c' 青色(cyan) 'm' 品红(magenta) 'y' 黄色(yellow) 'k' 黑色(black) 'w' 白色(white) fmt 举例: 12345'b' # 默认形状的蓝色标记'or' # 红圈'-g' # 绿色实线'--' # 默认颜色的虚线'^k:' # 黑色三角形标记,由虚线连接 【2x00】基本示例12345678910111213import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei'] # 设置显示中文x = np.arange(-2*np.pi, 2*np.pi, 0.01)y = np.sin(3*x)/xplt.title('线性图示例') # 设置标题plt.xlabel('x 轴') # 设置 x 轴标签plt.ylabel('y 轴') # 设置 y 轴标签plt.plot(x, y)plt.show() 【3x00】多条数据绘制多条数据,设置不同数据,然后多次调用 plt.plot() 函数即可,不同数据的线条颜色会不同,系统随机,可单独指定不同颜色。 123456789101112131415161718import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = np.arange(-2*np.pi, 2*np.pi, 0.01)y1 = np.sin(3*x)/xy2 = np.sin(2*x)/xy3 = np.sin(1*x)/xplt.title('多数据线性图示例')plt.xlabel('x 轴')plt.ylabel('y 轴')plt.plot(x, y1)plt.plot(x, y2)plt.plot(x, y3)plt.show() 【4x00】设置颜色 / 样式 / 图例设置线条颜色样式等属性直接在 plot() 函数里面添加相应参数即可,设置图例则需要调用 legend() 方法。 12345678910111213141516171819202122import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x1 = np.arange(-2*np.pi, 2*np.pi, 0.01)y1 = np.sin(3*x1)/x1y2 = np.sin(2*x1)/x1x3 = np.arange(-2*np.pi, 2*np.pi, 2)y3 = np.array([0, 2, 1.5, 1, 2.4, -0.2, 1.7])plt.title('线性图自定义样式示例')plt.xlabel('x 轴')plt.ylabel('y 轴')plt.plot(x1, y1, '--r', label='x1, y1') # 线条样式为 --,颜色为 r(红色)plt.plot(x1, y2, color='green', label='x1, y2') # 样式默认,颜色为绿色plt.plot(x3, y3, marker='o', mfc='r', linestyle=':', label='x3, y3') # 标记样式为 o,颜色为 r(红色),线条样式为 :plt.legend(edgecolor='#87A3CC', facecolor='#F5F5F5') # 图例plt.show() 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105839855未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【5x00】设置刻度调用 xticks() 和 yticks() 函数可以对坐标刻度进行自定义,该函数接收两个参数,第一个参数表示要显示的刻度位置,第二个参数表示在对应刻度线上要显示的标签信息,标签信息支持 LeTeX 数学公式,使用时要用美元符号 $ 包围起来。 12345678910111213141516171819202122import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = np.arange(-2*np.pi, 2*np.pi, 0.01)y1 = np.sin(3*x)/xy2 = np.sin(2*x)/xy3 = np.sin(1*x)/xplt.title('线性图设置刻度示例')plt.xlabel('x 轴')plt.ylabel('y 轴')plt.plot(x, y1, '--r', label='sin(3*x)/x')plt.plot(x, y2, color='green', linestyle=':', label='sin(2*x)/x')plt.plot(x, y3, label='sin(1*x)/x')plt.legend(edgecolor='#87A3CC', facecolor='#F5F5F5')plt.xticks((-2*np.pi, -np.pi, 0, np.pi, 2*np.pi), (r'$-2\\pi$', r'$-\\pi$', '$0$', r'$\\pi$', r'$2\\pi$'))plt.yticks((-1, 0, 1, 2, 3))plt.show() 【6x00】隐藏画布边框Matplotlib 所绘制的图表中的每个绘图元素,例如线条 Line2D、文字 Text、刻度等在内存中都有一个对象与之对应。 matplotlib.pyplot.gca() 函数用于获取当前的绘图区 Axes(Get Current Axes) matplotlib.pyplot.gcf() 函数用于获取当前的画布 Figure(Get Current Figure) 例如:matplotlib.pyplot.plot() 实际上会通过 matplotlib.pyplot.gca() 获得当前的 Axes 对象 ax,然后再调用 ax.plot() 方法实现真正的绘图。我们可以通过这种方法来实现画布边框的隐藏和坐标轴的移动。 12345678910111213141516171819202122232425262728import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = np.arange(-2*np.pi, 2*np.pi, 0.01)y1 = np.sin(3*x)/xy2 = np.sin(2*x)/xy3 = np.sin(1*x)/xplt.title('线性图隐藏画布边框示例')plt.xlabel('x 轴')plt.ylabel('y 轴')plt.plot(x, y1, '--r', label='sin(3*x)/x')plt.plot(x, y2, color='green', linestyle=':', label='sin(2*x)/x')plt.plot(x, y3, label='sin(1*x)/x')plt.legend(edgecolor='#87A3CC', facecolor='#F5F5F5')plt.xticks((-2*np.pi, -np.pi, 0, np.pi, 2*np.pi), (r'$-2\\pi$', r'$-\\pi$', '$0$', r'$\\pi$', r'$2\\pi$'))plt.yticks((-1, 0, 1, 2, 3))ax = plt.gca() # 获取当前的画布, gca = get current axesax.spines['right'].set_visible(False) # 获取绘图区的轴对象(spines),设置右边框不显示ax.spines['top'].set_visible(False) # 获取绘图区的轴对象(spines),设置上边框不显示# ax.spines['right'].set_color('none') # 设置颜色为 none,效果与上面的一致# ax.spines['top'].set_color('none')plt.show() 【7x00】移动坐标轴123456789101112131415161718192021222324252627282930313233import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = np.arange(-2*np.pi, 2*np.pi, 0.01)y1 = np.sin(3*x)/xy2 = np.sin(2*x)/xy3 = np.sin(1*x)/xplt.title('线性图移动坐标轴示例')plt.xlabel('x 轴')plt.ylabel('y 轴')plt.plot(x, y1, '--r', label='sin(3*x)/x')plt.plot(x, y2, color='green', linestyle=':', label='sin(2*x)/x')plt.plot(x, y3, label='sin(1*x)/x')plt.legend(edgecolor='#87A3CC', facecolor='#F5F5F5')plt.xticks((-2*np.pi, -np.pi, 0, np.pi, 2*np.pi), (r'$-2\\pi$', r'$-\\pi$', '$0$', r'$\\pi$', r'$2\\pi$'))plt.yticks((-1, 0, 1, 2, 3))ax = plt.gca() # 获取当前的画布, gca = get current axesax.spines['right'].set_visible(False) # 获取绘图区的轴对象(spines),设置右边框不显示ax.spines['top'].set_visible(False) # 获取绘图区的轴对象(spines),设置上边框不显示# ax.spines['right'].set_color('none') # 设置颜色为 none,效果与上面的一致# ax.spines['top'].set_color('none')ax.spines['left'].set_position(('data', 0)) # 设置两个坐标轴在(0, 0)位置相交ax.spines['bottom'].set_position(('data', 0))ax.xaxis.set_ticks_position('bottom') # 设置 x 坐标轴标签的位置ax.yaxis.set_ticks_position('left') # 设置 y 坐标轴标签的位置plt.show() 【8x00】指定位置显示文本matplotlib.pyplot.annotate() 方法可以在指定坐标点添加文本或 LaTeX 描述,也可以在其他位置添加描述后,使用箭头指向某个坐标点。 基本语法:matplotlib.pyplot.annotate(text, xy, xytext, xycoords, textcoords, ha, va, arrowprops, \\*\\*kwargs) 参数 描述 text str 类型,注释的文本 xy 被注释的坐标点,格式:(x, y) xytext 注释文本的坐标点,格式:(x, y),默认与 xy 相同 xycoords 被注释的坐标点的参考系,取值参见表一,默认为 ‘data’ textcoords 注释文本的坐标点的参考系,取值参见表二,默认为 xycoords 的值 ha 注释点在注释文本的左边、右边或中间(left、right、center) va 注释点在注释文本的上边、下边、中间或基线 (top、bottom、center、baseline) arrowprops dict 字典类型,箭头的样式如果 arrowprops 不包含键 arrowstyle,则允许的键参见表三如果 arrowprops 包含键 arrowstyle,则允许的键参见表四 表一:xycoords 取值类型 取值 描述 ‘figure points’ 以画布左下角为参考,单位为点数 ‘figure pixels’ 以画布左下角为参考,单位为像素 ‘figure fraction’ 以画布左下角为参考,单位为百分比 ‘axes points’ 以绘图区左下角为参考,单位为点数 ‘axes pixels’ 以绘图区左下角为参考,单位为像素 ‘axes fraction’ 以绘图区左下角为参考,单位为百分比 ‘data’ 使用被注释对象的坐标系,即数据的 x, y 轴(默认) ‘polar’ 使用(θ,r)形式的极坐标系 表二:textcoords 取值类型 取值 描述 ‘figure points’ 以画布左下角为参考,单位为点数 ‘figure pixels’ 以画布左下角为参考,单位为像素 ‘figure fraction’ 以画布左下角为参考,单位为百分比 ‘axes points’ 以绘图区左下角为参考,单位为点数 ‘axes pixels’ 以绘图区左下角为参考,单位为像素 ‘axes fraction’ 以绘图区左下角为参考,单位为百分比 ‘data’ 使用被注释对象的坐标系,即数据的 x, y 轴 ‘polar’ 使用(θ,r)形式的极坐标系 ‘offset points’ 相对于被注释点的坐标 xy 的偏移量,单位是点 ‘offset pixels’ 相对于被注释点的坐标 xy 的偏移量,单位是像素 表三:arrowprops 不包含键 arrowstyle 时的取值 键 描述 width 箭头的宽度,以点为单位 headwidth 箭头底部的宽度,以点为单位 headlength 箭头的长度,以点为单位 shrink 箭头两端收缩占总长的百分比 ? 其他 matplotlib.patches.FancyArrowPatch 中的关键字,部分常用关键字参见表五 表四:arrowprops 包含键 arrowstyle 时的取值 取值 描述 '-' None '->' head_length=0.4,head_width=0.2 '-[' widthB=1.0,lengthB=0.2,angleB=None ']-' widthA=1.0, lengthA=0.2, angleA=None ]-[ widthA=1.0, lengthA=0.2, angleA=None, widthB=1.0, lengthB=0.2, angleB=None `’ - ‘` widthA=1.0,widthB=1.0 `’- >’` head_length=0.4,head_width=0.2 '<-' head_length=0.4,head_width=0.2 '<->' head_length=0.4,head_width=0.2 `’< -‘` head_length=0.4,head_width=0.2 `’< - >’` head_length=0.4,head_width=0.2 'fancy' head_length=0.4,head_width=0.4,tail_width=0.4 'simple' head_length=0.5,head_width=0.5,tail_width=0.2 'wedge' tail_width=0.3,shrink_factor=0.5 表五:matplotlib.patches.FancyArrowPatch 常用的键 键 描述 arrowstyle 箭头样式,取值参见表四 connectionstyle 连接方式,默认为 arc3,有以下五种取值:angle:angleA=90, angleB=0, rad=0.0angle3:angleA=90, angleB=0arc:angleA=0, angleB=0, armA=None, armB=None, rad=0.0arc3:rad=0.0bar:armA=0.0, armB=0.0, fraction=0.3, angle=Noneangle 为箭头旋转的角度,rad 为弧度 relpos 箭头起始点相对注释文本的位置,默认为 (0.5, 0.5),即文本的中心(0,0)表示左下角,(1,1)表示右上角 patchA 箭头起点处的图形,默认为文本的边框 patchB 箭头终点处的图形,默认为空 shrinkA 箭头起点的缩进点数,默认为2 shrinkB 箭头终点的缩进点数,默认为2 ? 其他键值,参见官方文档 matplotlib.patches.PathPatch connectionstyle 样式举例 应用举例: 1234567891011121314151617181920212223242526272829303132333435363738394041import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = np.arange(-2*np.pi, 2*np.pi, 0.01)y1 = np.sin(3*x)/xy2 = np.sin(2*x)/xy3 = np.sin(1*x)/xplt.title('线性图显示文本注释示例')plt.xlabel('x 轴')plt.ylabel('y 轴')plt.plot(x, y1, '--r', label='sin(3*x)/x')plt.plot(x, y2, color='green', linestyle=':', label='sin(2*x)/x')plt.plot(x, y3, label='sin(1*x)/x')plt.legend(edgecolor='#87A3CC', facecolor='#F5F5F5')plt.xticks((-2*np.pi, -np.pi, 0, np.pi, 2*np.pi), (r'$-2\\pi$', r'$-\\pi$', '$0$', r'$\\pi$', r'$2\\pi$'))plt.yticks((-1, 0, 1, 2, 3))ax = plt.gca() # 获取当前的画布, gca = get current axesax.spines['right'].set_visible(False) # 获取绘图区的轴对象(spines),设置右边框不显示ax.spines['top'].set_visible(False) # 获取绘图区的轴对象(spines),设置上边框不显示# ax.spines['right'].set_color('none') # 设置颜色为 none,效果与上面的一致# ax.spines['top'].set_color('none')ax.spines['left'].set_position(('data', 0)) # 设置两个坐标轴在(0, 0)位置相交ax.spines['bottom'].set_position(('data', 0))ax.xaxis.set_ticks_position('bottom') # 设置 x 坐标轴标签的位置ax.yaxis.set_ticks_position('left') # 设置 y 坐标轴标签的位置plt.annotate(r'$\\lim_{x\\to 0}\\frac{\\sin(x)}{x}=1$', # 插入 LaTeX 表达式 xy=[0, 1], # 被标记的坐标 xycoords='data', # 被标记的坐标的参考系 xytext=[30, 30], # 注释文本的坐标 textcoords='offset points', # 注释文本的坐标的参考系 fontsize=16, # 字体大小 arrowprops=dict(arrowstyle=\"->\", connectionstyle=\"arc3, rad=.2\")) # 箭头样式plt.show() 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105839855未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Matplotlib","slug":"Python-数据分析/Matplotlib","permalink":"https://www.itrhx.com/categories/Python-数据分析/Matplotlib/"}],"tags":[{"name":"Matplotlib","slug":"Matplotlib","permalink":"https://www.itrhx.com/tags/Matplotlib/"},{"name":"线性图","slug":"线性图","permalink":"https://www.itrhx.com/tags/线性图/"}]},{"title":"Python 数据分析三剑客之 Matplotlib(三):图例/LaTeX/刻度/子图/补丁等基本图像属性","slug":"A70-Matplotlib-03","date":"2020-04-14T13:36:35.265Z","updated":"2020-08-06T03:09:34.783Z","comments":true,"path":"2020/04/14/A70-Matplotlib-03/","link":"","permalink":"https://www.itrhx.com/2020/04/14/A70-Matplotlib-03/","excerpt":"","text":"Matplotlib 系列文章: Python 数据分析三剑客之 Matplotlib(一):初识 Matplotlib 与其 matplotibrc 配置文件 Python 数据分析三剑客之 Matplotlib(二):文本描述 / 中文支持 / 画布 / 网格等基本图像属性 Python 数据分析三剑客之 Matplotlib(三):图例 / LaTeX / 刻度 / 子图 / 补丁等基本图像属性 Python 数据分析三剑客之 Matplotlib(四):线性图的绘制 Python 数据分析三剑客之 Matplotlib(五):散点图的绘制 Python 数据分析三剑客之 Matplotlib(六):直方图 / 柱状图 / 条形图的绘制 Python 数据分析三剑客之 Matplotlib(七):饼状图的绘制 Python 数据分析三剑客之 Matplotlib(八):等高线 / 等值线图的绘制 Python 数据分析三剑客之 Matplotlib(九):极区图 / 极坐标图 / 雷达图的绘制 Python 数据分析三剑客之 Matplotlib(十):3D 图的绘制 Python 数据分析三剑客之 Matplotlib(十一):最热门最常用的 50 个图表【译文】 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105828143未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【1x00】设置图例matplotlib.pyplot.legend() 方法可以为图表设置图例。 基本语法:matplotlib.pyplot.legend(\\*args, \\*\\*kwargs) 部分常见参数: 参数 描述 loc 图例在画布中的位置,默认为 best,其他取值:best, upper right, upper left, lower left lower right, right, center left, center right lower center, upper center, center也可以用数字 0 - 10 来表示上述位置 bbox_to_anchor 调整图例在画布中的位置,当 loc 达不到我们想要的效果时,就可以使用该参数该参数接收一个二元数组 (x, y),x 用于控制图例的左右移动,值越大越向右边移动y 用于控制图例的上下移动,值越大,越向上移动 borderaxespad 图例距离轴之间的距离,float 类型,默认为 0.5 borderpad 图例边框空白区域大小,float 类型,默认为 0.4 columnspacing 图例列间距,float 类型,默认为 2.0 edgecolor 图例边缘线颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo facecolor 图例背景颜色,默认继承自 axes.facecolor其他颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo fancybox 是否使用圆形框作为图例背景, 默认为 True fontsize 图例字体大小,默认为 medium,xx-small, x-small, small, medium large, x-large, xx-large, smaller, larger也可以使用数字来表示字体大小 framealpha 图例透明度,float 类型,默认为 0.8,取值范围:[0, 1] handleheight 图例的高度 ,float 类型,默认为 0.7 handlelength 图例的宽度,float 类型,默认为 2.0 handletextpad 图例和图例文本之间的水平距离,float 类型,默认为 0.8 labelspacing 不同图例之间的垂直距离,float 类型,默认为 0.5 shadow 是否给图例添加阴影效果,默认为 False 【1x01】方法一:指定 label 参数在画图的时候先指定 label 标签文本,再调用 legend() 方法即可。 12345678910111213import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = range(2, 26, 2)y = range(0, 12)a = [5, 10, 15, 20, 25, 30]b = [3, 4, 5, 6, 7, 8]plt.plot(a, b, label='图例一') # 指定 a,b 数据的图例plt.plot(x, y, label='图例二') # 指定 x,y 数据的图例plt.legend(loc=2, edgecolor='red', facecolor='#F5F5F5') # 指定图例位置、边缘线条颜色和背景色plt.show() 【1x02】方法二:使用 set_label 方法在画图的时候先使用 set_label() 方法指定标签文本,再调用 legend() 方法即可。 123456789101112131415import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = range(2, 26, 2)y = range(0, 12)a = [5, 10, 15, 20, 25, 30]b = [3, 4, 5, 6, 7, 8]line1, = plt.plot(a, b)line2, = plt.plot(x, y)line1.set_label('图例一') # 指定 a,b 数据的图例line2.set_label('图例二') # 指定 x,y 数据的图例plt.legend(loc=2, edgecolor='red', facecolor='#F5F5F5') # 指定图例位置、边缘线条颜色和背景色plt.show() 【1x03】方法三:直接使用 legend 方法直接使用 legend() 方法来指定图例标签也可以达到同样效果,图例以列表或者元组形式储存,图例与绘制图形的顺序一一对应。 12345678910111213import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = range(2, 26, 2)y = range(0, 12)a = [5, 10, 15, 20, 25, 30]b = [3, 4, 5, 6, 7, 8]plt.plot(a, b)plt.plot(x, y)plt.legend(['图例一', '图例二'], loc=2, edgecolor='red', facecolor='#F5F5F5')plt.show() 也可以使用两个元组,将绘制的图形和图例一一对应来储存: 12345678910111213import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = range(2, 26, 2)y = range(0, 12)a = [5, 10, 15, 20, 25, 30]b = [3, 4, 5, 6, 7, 8]line1, = plt.plot(a, b)line2, = plt.plot(x, y)plt.legend((line1, line2), ('图例一', '图例二'), loc=2, edgecolor='red', facecolor='#F5F5F5')plt.show() 以上三种方法绘制的图形均一致: 【2x00】数学公式 LaTeXLaTeX(LATEX,音译“拉泰赫”)是一种基于 TeX 的排版系统,常用于生成复杂表格和数学公式,Matplotlib 提供了自己的 TeX 表达式解析器,布局引擎和字体,布局引擎基于 Donald Knuth 的 TeX 布局算法改编。使用数学公式时用 $ 将其包围起来即可。具体的符号与其对应的英文表示参见官方文档:https://matplotlib.org/tutorials/text/mathtext.html 应用举例: 1234567891011121314import numpy as npimport matplotlib.pyplot as pltt = np.arange(0.0, 2.0, 0.01)s = np.sin(2*np.pi*t)plt.title(r'$\\alpha_i > \\beta_i$', fontsize=20)plt.text(1, -0.6, r'$\\sum_{i=0}^\\infty x_i$', fontsize=20)plt.text(0.6, 0.6, r'$\\mathcal{A}\\mathrm{sin}(2 \\omega t)$', fontsize=20)plt.xlabel('time (s)')plt.ylabel('volts (mV)')plt.plot(t, s)plt.show() 【3x00】调整 x / y 轴刻度和范围在生成图像时,默认会按照所给的数据均匀设置几个刻度,如果对默认的刻度不满意,则可以使用 xticks() 或 yticks() 方法指定刻度值。xlim() 与 ylim() 则可以设置刻度的范围。 基本语法:matplotlib.pyplot.xticks([ticks=None, labels=None, \\*\\*kwargs])matplotlib.pyplot.yticks([ticks=None, labels=None, \\*\\*kwargs]) 参数 描述 ticks 数组形式的位置列表,即显示第 n 个位置的刻度,可选项,若传递空列表将删除所有 xtick / ytick labels 数组形式的值,在对应刻度线显示的标签信息。仅当同时传递了刻度时,才能传递此参数 **kwargs 其他参数参见 Text 其他参数里面有一个常用的 rotation 参数,次参数可以用于设置刻度标签的旋转角度,对于标签太长的可以将其旋转一个角度来显示。 应用举例: 1234567891011import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = range(2, 26, 2)y = range(0, 12)plt.plot(x, y)# x 轴每隔三个显示一次刻度,旋转45°显示标签plt.xticks(range(2, 26, 3), ('the {} ticks'.format(i) for i in range(2, 26, 3)), rotation=45)plt.show() 12345678910import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = range(2, 26, 2)y = range(0, 12)plt.xlim((0, 30)) # 设置 x 轴刻度范围plt.plot(x, y)plt.show() 【4x00】画布边框与坐标轴的移动Matplotlib 所绘制的图表中的每个绘图元素,例如线条 Line2D、文字 Text、刻度等在内存中都有一个对象与之对应。 matplotlib.pyplot.gca() 函数用于获取当前的绘图区 Axes(Get Current Axes) matplotlib.pyplot.gcf() 函数用于获取当前的画布 Figure(Get Current Figure) 例如:matplotlib.pyplot.plot() 实际上会通过 matplotlib.pyplot.gca() 获得当前的 Axes对象 ax,然后再调用 ax.plot() 方法实现真正的绘图。我们可以通过这种方法来实现画布边框的隐藏和坐标轴的移动。 应用举例: 12345678910111213141516171819202122232425import matplotlib.pyplot as pltimport numpy as npx = np.arange(0, 2*np.pi, np.pi/100)y = np.sin(x)plt.plot(x, y)plt.xlabel('X axis')plt.ylabel('Y axis')ticks = (0, np.pi/2, np.pi, 3*np.pi/2, 2*np.pi)labels = ('0', r'$\\frac{\\pi} {2}$', r'$\\pi$', r'$\\frac{3\\pi} {2}$', r'$2\\pi$')plt.xticks(ticks, labels) # 设置 x 坐标轴显示的数据ax = plt.gca() # 获取当前的画布, gca = get current axesax.spines['right'].set_visible(False) # 设置右边框不显示ax.spines['top'].set_visible(False) # 设置上边框不显示# ax.spines['top'].set_color('none') # 设置颜色为无也可以ax.xaxis.set_ticks_position('bottom') # 设置 x 坐标轴的标签位置ax.yaxis.set_ticks_position('left') # 设置 y 坐标轴的标签位置ax.spines['bottom'].set_position(('data', 0)) # 设置 x 轴在 (0, 0) 位置ax.spines['left'].set_position(('data', 0)) # 设置 y 轴在 (0, 0) 位置plt.show() 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105828143未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【5x00】创建子图子图的概念:在同一张画布中创建多个图像,方便对数据进行对比。 【5x01】方法一:add_subplot()首先创建一个画布,然后利用 add_subplot() 方法填充子图,该方法接收三个参数,前两个参数表示子图有几行几列,最后一个参数表示第几个子图,如:fig.add_subplot(221) 表示总共有两行两列(2x2=4)一共4个子图,当前是第一个子图。若子图大于9个则用逗号隔开即可。 应用举例: 1234567891011121314151617181920import numpy as npimport matplotlib.pyplot as pltx = np.arange(100)fig = plt.figure(figsize=(12, 6))ax1 = fig.add_subplot(221) # 第 1 个子图ax1.plot(x, x)ax2 = fig.add_subplot(222) # 第 2 个子图ax2.plot(x, -x)ax3 = fig.add_subplot(223) # 第 3 个子图ax3.plot(x, x ** 2)ax4 = fig.add_subplot(224) # 第 4 个子图ax4.plot(-x, x ** 2)plt.show() 【5x02】方法二:pyplot.subplot()matplotlib.pyplot.subplot() 方法和 add_subplot() 方法有点儿类似,同样接收三个参数,前两个参数表示子图有几行几列,最后一个参数表示第几个子图。 1234567891011121314151617181920import numpy as npimport matplotlib.pyplot as pltx = np.arange(100)plt.figure(figsize=(12, 6))plt.subplot(221) # 第 1 个子图plt.plot(x, x)plt.subplot(222) # 第 2 个子图plt.plot(x, x ** 2)plt.subplot(223) # 第 3 个子图plt.plot(x, x ** 3)plt.subplot(224) # 第 4 个子图plt.plot(x, x ** 4)plt.show() 【5x03】方法三:pyplot.subplots()matplotlib.pyplot.subplots() 函数会将画布分割成指定的列和行,分割后依次在各个区域画图即可。注意与 matplotlib.pyplot.subplot() 略有差别。 fig, axes = plt.subplots 的意思是:plt.subplots 方法会返回一个包含 figure(画布) 和 axes(绘图区) 对象的元组,fig 和 axes 参数分别接收这两个对象,后期对不同绘图区进行处理即可。 123456789101112import numpy as npimport matplotlib.pyplot as pltx = np.arange(100)fig, axes = plt.subplots(figsize=(12, 6), nrows=2, ncols=2) # 将画布分割为2行2列,起始值为0axes[0][0].plot(x, x) # 绘制第1行第1列axes[0][1].plot(x, -x) # 绘制第1行第2列axes[1][0].plot(-x, x ** 2) # 绘制第2行第1列axes[1][1].plot(x, -x ** 2) # 绘制第2行第2列plt.show() 【6x00】填充补丁matplotlib.patches 可用于在画布上填充圆形、长方形、椭圆形、多边形等多种图像补丁。 官方文档:https://matplotlib.org/api/patches_api.html 类 描述 matplotlib.patches.Arc(xy, width, height, angle=0.0, theta1=0.0, theta2=360.0, **kwargs) 椭圆弧 matplotlib.patches.Arrow(x, y, dx, dy, width=1.0, **kwargs) 箭头 matplotlib.patches.Circle(xy, radius=5, **kwargs) 圆 matplotlib.patches.Ellipse(xy, width, height, angle=0, **kwargs) 椭圆 matplotlib.patches.CirclePolygon(xy, radius=5, resolution=20, **kwargs) 近似多边形的圆形面片 matplotlib.patches.Polygon(xy, closed=True, **kwargs) 不规则多边形 matplotlib.patches.Rectangle(xy, width, height, angle=0.0, **kwargs) 矩形 matplotlib.patches.RegularPolygon(xy, numVertices, radius=5, orientation=0, **kwargs) 正多边形 matplotlib.patches.Shadow(patch, ox, oy, props=None, **kwargs) 创建给定补丁的阴影 matplotlib.patches.Wedge(center, r, theta1, theta2, width=None, **kwargs) 楔形 应用举例: 1234567891011121314151617181920212223242526272829303132333435363738import numpy as npimport matplotlib.pyplot as pltimport matplotlib.patches as mpathesx = np.arange(0.0, 2.0, 0.01)y = np.sin(2*np.pi*x)# 获取当前绘图区(gca = Get Current Axesax = plt.gca()# 圆形:圆点(0.2, -0.25),半径0.2,红色circle = mpathes.Circle((0.2, -0.25), 0.2, color='r')# 长方形:左侧和底部坐标(0.25, 0.75),宽0.25,高0.15,透明度0.5rect = mpathes.Rectangle((0.25, 0.75), 0.25, 0.15, alpha=0.5)# 正多边形:中心点坐标(1.0, 0),顶点数6,中心到每个顶点的距离0.25regular_polygon = mpathes.RegularPolygon((1.0, 0), 6, 0.25, color='g')# 不规则多边形:polygon_point 为要连接的点的坐标polygon_point = [[1.5, -0.75], [1.75, -1], [2.0, 0], [1.5, -0.25]]polygon = mpathes.Polygon(polygon_point, color='#FF69B4', alpha=0.3)# 椭圆形:中心点坐标(1.25, 0.75),横轴长度0.4,垂直轴长度0.2ellipse = mpathes.Ellipse((1.25, 0.75), 0.4, 0.2, color='y')# 将补丁添加到当前绘图区ax.add_patch(circle)ax.add_patch(rect)ax.add_patch(regular_polygon)ax.add_patch(polygon)ax.add_patch(ellipse)plt.xlabel('x axis label')plt.ylabel('y axis label')plt.grid()plt.plot(x, y)plt.show() 【7x00】保存图像matplotlib.pyplot.savefig() 方法可以将绘制的图像保存到本地,支持多种格式:eps, pdf, pgf, png, ps, raw, rgba, svg, svgz。 注意:因为调用 plt.show() 函数后,会创建一个新的空白的图片,所以在保存图片时注意要在 plt.show() 前调用 plt.savefig() 基本语法:matplotlib.pyplot.savefig(fname, dpi=None, facecolor='w', edgecolor='w', format=None, transparent=False) 参数 描述 fname str 类型 / 文件路径 / 类似文件的对象如果未设置格式,则根据 fname 的扩展名(如果有)和 rcParams[“savefig.format”] = ‘png’ 推断输出格式如果设置了格式,则它将确定输出格式 dpi 保存图片的像素(dpi),以每英寸点数为单位。如果为 None,则默认取 rcParams[’savefig.dpi’] = ‘figure’ facecolor 保存图片的画布颜色,默认为 white edgecolor 保存图片的边缘颜色,默认为 white format 保存图片的格式,未设置则取 fname 中的格式 transparent 保存图片的背景是否透明 应用举例: 1234567891011121314import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = range(2, 26, 2)y = range(0, 12)a = [5, 10, 15, 20, 25, 30]b = [3, 4, 5, 6, 7, 8]line1, = plt.plot(a, b)line2, = plt.plot(x, y)plt.legend((line1, line2), ('图例一', '图例二'), loc=2, edgecolor='red', facecolor='#F5F5F5')plt.savefig('D:\\\\data\\\\pic.png', transparent=True) # 保存为透明文件plt.show() 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105828143未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Matplotlib","slug":"Python-数据分析/Matplotlib","permalink":"https://www.itrhx.com/categories/Python-数据分析/Matplotlib/"}],"tags":[{"name":"Matplotlib","slug":"Matplotlib","permalink":"https://www.itrhx.com/tags/Matplotlib/"},{"name":"图例","slug":"图例","permalink":"https://www.itrhx.com/tags/图例/"},{"name":"LaTeX","slug":"LaTeX","permalink":"https://www.itrhx.com/tags/LaTeX/"},{"name":"子图","slug":"子图","permalink":"https://www.itrhx.com/tags/子图/"},{"name":"补丁","slug":"补丁","permalink":"https://www.itrhx.com/tags/补丁/"}]},{"title":"Python 数据分析三剑客之 Matplotlib(二):文本描述/中文支持/画布/网格等基本图像属性","slug":"A69-Matplotlib-02","date":"2020-04-12T12:16:52.040Z","updated":"2020-08-06T03:07:49.826Z","comments":true,"path":"2020/04/12/A69-Matplotlib-02/","link":"","permalink":"https://www.itrhx.com/2020/04/12/A69-Matplotlib-02/","excerpt":"","text":"Matplotlib 系列文章: Python 数据分析三剑客之 Matplotlib(一):初识 Matplotlib 与其 matplotibrc 配置文件 Python 数据分析三剑客之 Matplotlib(二):文本描述 / 中文支持 / 画布 / 网格等基本图像属性 Python 数据分析三剑客之 Matplotlib(三):图例 / LaTeX / 刻度 / 子图 / 补丁等基本图像属性 Python 数据分析三剑客之 Matplotlib(四):线性图的绘制 Python 数据分析三剑客之 Matplotlib(五):散点图的绘制 Python 数据分析三剑客之 Matplotlib(六):直方图 / 柱状图 / 条形图的绘制 Python 数据分析三剑客之 Matplotlib(七):饼状图的绘制 Python 数据分析三剑客之 Matplotlib(八):等高线 / 等值线图的绘制 Python 数据分析三剑客之 Matplotlib(九):极区图 / 极坐标图 / 雷达图的绘制 Python 数据分析三剑客之 Matplotlib(十):3D 图的绘制 Python 数据分析三剑客之 Matplotlib(十一):最热门最常用的 50 个图表【译文】 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105828049未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【1x00】添加文本描述【1x01】添加标题:matplotlib.pyplot.title()matplotlib.pyplot.title() 方法可为图表添加标题。 基本语法:matplotlib.pyplot.title(label[, fontdict=None, loc=None, pad=None]) 参数 描述 label str 类型,标题文字 fontdict 字典类型,控制标题文本外观,可选项,默认值为:{'fontsize': rcParams['axes.titlesize'],'fontweight' : rcParams['axes.titleweight'],'color' : rcParams['axes.titlecolor'],'verticalalignment': 'baseline','horizontalalignment': loc} loc str 类型,可选项,三个可选值:center、left、right,默认为 rcParams["axes.titlelocation"](默认为 center) pad float 类型,可选项,标题距轴顶部的偏移量(以磅为单位)。如果为 None,则默认为 rcParams["axes.titlepad"](默认为:6.0) 应用举例: 1234567import matplotlib.pyplot as pltx = range(2, 26, 2)y = range(0, 12)plt.title('This is a title')plt.plot(x, y)plt.show() 【1x02】为坐标轴添加标签:matplotlib.pyplot.xlabel() / ylabel()matplotlib.pyplot.xlabel():为 x 轴添加标签;matplotlib.pyplot.ylabel():为 y 轴添加标签。 基本语法:matplotlib.pyplot.xlabel(xlabel[, fontdict=None, labelpad=None])matplotlib.pyplot.ylabel(ylabel[, fontdict=None, labelpad=None]) 参数 描述 xlabel / ylabel str 类型,要添加的文本信息 fontdict 字典类型,控制标题文本外观,可选项,默认值为:{'fontsize': rcParams['axes.titlesize'],'fontweight' : rcParams['axes.titleweight'],'color' : rcParams['axes.titlecolor'],'verticalalignment': 'baseline','horizontalalignment': loc} labelpad float 类型,可选项,x 轴标签距离 x 轴的距离 应用举例: 123456789101112131415import matplotlib.pyplot as pltx = range(2, 26, 2)y = range(0, 12)a = [5, 10, 15, 20, 25, 30]b = [3, 4, 5, 6, 7, 8]plt.title('This is a title')plt.xlabel('This is x label', fontdict={'fontsize': 15, 'fontweight': 'bold', 'color': 'red'}, labelpad=15.0)plt.ylabel('This is y label', fontsize=10, fontweight='light', color='blue', labelpad=15.0)plt.plot(x, y)plt.plot(a, b)plt.show() 【1x03】任意位置添加文本:matplotlib.pyplot.text()matplotlib.pyplot.text() 方法可以在画布上任意位置添加文本描述。 基本语法:matplotlib.pyplot.text(x, y, s[, fontdict=None, withdash=<deprecated parameter>]) 参数 描述 x, y 放置文本的坐标位置 s str 类型,要添加的文本信息 fontdict 字典类型,控制标题文本外观,可选项,默认值为:{'fontsize': rcParams['axes.titlesize'],'fontweight' : rcParams['axes.titleweight'],'color' : rcParams['axes.titlecolor'],'verticalalignment': 'baseline','horizontalalignment': loc} ha 注释点在注释文本的左边、右边或中间(left、right、center) va 注释点在注释文本的上边、下边、中间或基线 (top、bottom、center、baseline) withdash bool 类型,可选项,默认为 False,创建一个 TextWithDash 实例而不是一个 Text 实例 应用举例: 1234567891011121314151617181920import matplotlib.pyplot as pltplt.rcParams['lines.marker'] = 'o' # 设置线条上点的形状a = [5, 10, 15, 20, 25, 30]b = [3, 4, 5, 6, 7, 8]plt.title('This is a title')plt.xlabel('This is x label')plt.ylabel('This is y label')plt.text(4, 3.2, 'text1')plt.text(9, 4.2, 'text2')plt.text(14, 5.2, 'text3')plt.text(19, 6.2, 'text4')plt.text(24, 7.2, 'text5')plt.text(27.5, 7.9, 'text6')plt.plot(a, b)plt.show() 【1x03】任意位置添加文本:matplotlib.pyplot.annotate()matplotlib.pyplot.annotate() 方法可以在指定坐标点添加文本或 LaTeX 描述,也可以在其他位置添加描述后,使用箭头指向某个坐标点。比 matplotlib.pyplot.text() 更高级。 基本语法:matplotlib.pyplot.annotate(text, xy, xytext, xycoords, textcoords, ha, va, arrowprops, \\*\\*kwargs) 参数 描述 text str 类型,注释的文本 xy 被注释的坐标点,格式:(x, y) xytext 注释文本的坐标点,格式:(x, y),默认与 xy 相同 xycoords 被注释的坐标点的参考系,取值参见表一,默认为 ‘data’ textcoords 注释文本的坐标点的参考系,取值参见表二,默认为 xycoords 的值 ha 注释点在注释文本的左边、右边或中间(left、right、center) va 注释点在注释文本的上边、下边、中间或基线 (top、bottom、center、baseline) arrowprops dict 字典类型,箭头的样式如果 arrowprops 不包含键 arrowstyle,则允许的键参见表三如果 arrowprops 包含键 arrowstyle,则允许的键参见表四 表一:xycoords 取值类型 取值 描述 ‘figure points’ 以画布左下角为参考,单位为点数 ‘figure pixels’ 以画布左下角为参考,单位为像素 ‘figure fraction’ 以画布左下角为参考,单位为百分比 ‘axes points’ 以绘图区左下角为参考,单位为点数 ‘axes pixels’ 以绘图区左下角为参考,单位为像素 ‘axes fraction’ 以绘图区左下角为参考,单位为百分比 ‘data’ 使用被注释对象的坐标系,即数据的 x, y 轴(默认) ‘polar’ 使用(θ,r)形式的极坐标系 表二:textcoords 取值类型 取值 描述 ‘figure points’ 以画布左下角为参考,单位为点数 ‘figure pixels’ 以画布左下角为参考,单位为像素 ‘figure fraction’ 以画布左下角为参考,单位为百分比 ‘axes points’ 以绘图区左下角为参考,单位为点数 ‘axes pixels’ 以绘图区左下角为参考,单位为像素 ‘axes fraction’ 以绘图区左下角为参考,单位为百分比 ‘data’ 使用被注释对象的坐标系,即数据的 x, y 轴 ‘polar’ 使用(θ,r)形式的极坐标系 ‘offset points’ 相对于被注释点的坐标 xy 的偏移量,单位是点 ‘offset pixels’ 相对于被注释点的坐标 xy 的偏移量,单位是像素 表三:arrowprops 不包含键 arrowstyle 时的取值 键 描述 width 箭头的宽度,以点为单位 headwidth 箭头底部的宽度,以点为单位 headlength 箭头的长度,以点为单位 shrink 箭头两端收缩占总长的百分比 ? 其他 matplotlib.patches.FancyArrowPatch 中的关键字,部分常用关键字参见表五 表四:arrowprops 包含键 arrowstyle 时的取值 取值 描述 '-' None '->' head_length=0.4,head_width=0.2 '-[' widthB=1.0,lengthB=0.2,angleB=None ']-' widthA=1.0, lengthA=0.2, angleA=None ]-[ widthA=1.0, lengthA=0.2, angleA=None, widthB=1.0, lengthB=0.2, angleB=None `’ - ‘` widthA=1.0,widthB=1.0 `’- >’` head_length=0.4,head_width=0.2 '<-' head_length=0.4,head_width=0.2 '<->' head_length=0.4,head_width=0.2 `’< -‘` head_length=0.4,head_width=0.2 `’< - >’` head_length=0.4,head_width=0.2 'fancy' head_length=0.4,head_width=0.4,tail_width=0.4 'simple' head_length=0.5,head_width=0.5,tail_width=0.2 'wedge' tail_width=0.3,shrink_factor=0.5 表五:matplotlib.patches.FancyArrowPatch 常用的键 键 描述 arrowstyle 箭头样式,取值参见表四 connectionstyle 连接方式,默认为 arc3,有以下五种取值:angle:angleA=90, angleB=0, rad=0.0angle3:angleA=90, angleB=0arc:angleA=0, angleB=0, armA=None, armB=None, rad=0.0arc3:rad=0.0bar:armA=0.0, armB=0.0, fraction=0.3, angle=Noneangle 为箭头旋转的角度,rad 为弧度 relpos 箭头起始点相对注释文本的位置,默认为 (0.5, 0.5),即文本的中心(0,0)表示左下角,(1,1)表示右上角 patchA 箭头起点处的图形,默认为文本的边框 patchB 箭头终点处的图形,默认为空 shrinkA 箭头起点的缩进点数,默认为2 shrinkB 箭头终点的缩进点数,默认为2 ? 其他键值,参见官方文档 matplotlib.patches.PathPatch connectionstyle 样式举例 应用举例: 1234567891011121314151617181920import numpy as npimport matplotlib.pyplot as pltx = np.arange(-2*np.pi, 2*np.pi, 0.01)y = np.sin(1*x)/xplt.title('This is a title')plt.xlabel('This is x label')plt.ylabel('This is y label')plt.plot(x, y)plt.annotate(r'$\\lim_{x\\to 0}\\frac{\\sin(x)}{x}=1$', # 插入 LaTeX 表达式 xy=[0, 1], # 被标记的坐标 xycoords='data', # 被标记的坐标的参考系 xytext=[50, -40], # 注释文本的坐标 textcoords='offset points', # 注释文本的坐标的参考系 fontsize=16, # 字体大小 arrowprops=dict(arrowstyle=\"->\", connectionstyle=\"arc3, rad=.2\")) # 箭头样式plt.show() 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105828049未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【2x00】设置中文显示【2x01】常见系统自带文字及其英文名称Windows 系统中常见自带字体: 字体 英文名称 黑体 SimHei 宋体 SimSun 新宋体 NSimSun 仿宋 FangSong 仿宋_GB2312 FangSong_GB2312 楷体_GB2312 KaiTi_GB2312 楷体 KaiTi 微软正黑 Microsoft JhengHei 微软雅黑 Microsoft YaHei 细明体 MingLiU 标楷体 DFKai-SB 新细明体 PMingLiU 装有 office 后新添加的字体: 字体 英文名称 隶书 LiSu 幼圆 YouYuan 华文细黑 STXihei 华文楷体 STKaiti 华文宋体 STSong 华文中宋 STZhongsong 华文仿宋 STFangsong 方正舒体 FZShuTi 方正姚体 FZYaoti 华文彩云 STCaiyun 华文琥珀 STHupo 华文隶书 STLiti 华文行楷 STXingkai 华文新魏 STXinwei Mac OS 系统中常见自带字体: 字体 英文名称 华文细黑 STHeiti Light / STXihei 华文黑体 STHeiti 华文楷体 STKaiti 华文宋体 STSong 华文仿宋 STFangsong 丽黑 Pro LiHei Pro Medium 丽宋 Pro LiSong Pro Light 标楷体 BiauKai 苹果丽中黑 Apple LiGothic Medium 苹果丽细宋 Apple LiSung Light 【2x02】指定全局字体:rcParams通过 rcParams['font.sans-serif'] 可以配置全局字体。 优点:只需设置一次即可显示所有中文;缺点:污染全局,无法对单个中文设置字体。 应用举例: 12345678910111213import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei'] # 配置全局字体为微软雅黑plt.rcParams['axes.unicode_minus'] = False # 部分字体负号会显示乱码,可添加此参数进行配置a = [-15, -10, -5, 20, 25, 30]b = [-5, -4, -3, 6, 7, 8]plt.title('这是中文标题')plt.xlabel('这是 x 轴标签')plt.ylabel('这是 y 轴标签')plt.plot(a, b)plt.show() 【2x03】指定单个字体:fontpropertiesfontproperties 参数可以加在要设置中文的地方 优点:不污染全局;缺点:中文太多了挨个设置比较繁琐。 应用举例: 1234567891011import matplotlib.pyplot as plta = [-15, -10, -5, 20, 25, 30]b = [-5, -4, -3, 6, 7, 8]plt.title('这是中文标题', fontproperties='Microsoft JhengHei') # 微软正黑plt.xlabel('这是 x 轴标签', fontproperties='STLiti') # 华文隶书plt.ylabel('这是 y 轴标签', fontproperties='Microsoft YaHei') # 微软雅黑plt.plot(a, b)plt.show() 【2x04】指定文字路径:FontPropertiesmatplotlib 中 font_manager 模块的 FontProperties 方法可以通过指定文字路径来使用本地文字,在 Windows 中,文字路径一般是 C:\\Windows\\Fonts\\,文字名称可以通过其属性来获取,部分用户自己安装的字体可能包含多个类型,可打开字体合集后通过其属性来获取。 12345678910111213import matplotlib.pyplot as pltfrom matplotlib.font_manager import FontPropertiesfont = FontProperties(fname=r\"C:\\Windows\\Fonts\\STXINGKA.TTF\", size=14)a = [-15, -10, -5, 20, 25, 30]b = [-5, -4, -3, 6, 7, 8]plt.title('这是中文标题', fontproperties=font)plt.xlabel('这是 x 轴标签', fontproperties=font)plt.ylabel('这是 y 轴标签', fontproperties=font)plt.plot(a, b)plt.show() 【2x05】文字更多属性:rcrc 参数支持文字的更多属性设置,如字体粗细、大小等,这种方法同样将影响全局。 官方参考:https://matplotlib.org/api/matplotlib_configuration_api.html?highlight=rc#matplotlib.rc 应用举例: 1234567891011121314151617import matplotlib.pyplot as pltfont = {'family': 'SimHei', 'weight': 'bold', 'size': '10'}plt.rc('font', **font) # 设置字体的更多属性plt.rc('axes', unicode_minus=False) # 显示负号a = [-15, -10, -5, 20, 25, 30]b = [-5, -4, -3, 6, 7, 8]plt.title('这是中文标题')plt.xlabel('这是 x 轴标签')plt.ylabel('这是 y 轴标签')plt.plot(a, b)plt.show() 【3x00】设置画布大小 / 分辨率 / 颜色matplotlib.pyplot.figure() 可以设置画布的大小、图片分辨率、颜色等。 基本语法:matplotlib.pyplot.figure(figsize=None, dpi=None, facecolor=None, edgecolor=None, frameon=True, \\*\\*kwargs) 参数 描述 figsize (float, float) 的格式,代表宽度和高度,单位为英寸默认为 rcParams["figure.figsize"] = [6.4, 4.8],即:640 x 480 dpi 图像分辨率,默认为 rcParams["figure.figsize"] = 100 facecolor 图像背景颜色,默认为 rcParams["figure.edgecolor"] = ‘white’ edgecolor 图像边缘颜色,默认为 rcParams[’figure.edgecolor’] = ‘white’ frameon 是否启用图框 应用举例: 12345678910import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = range(2, 26, 2)y = range(0, 12)plt.figure(figsize=(6.5, 5), dpi=120, facecolor='#BBFFFF')plt.plot(x, y)plt.show() 【4x00】设置网格matplotlib.pyplot.grid() 方法可以为图表设置网格显示。 基本语法:matplotlib.pyplot.grid([b=None, which='major', axis='both', \\*\\*kwargs]) 参数 属性 b bool 值,可选项,是否显示网格,值为 None 或 True 则显示,False 不显示 which 可选项,在主/次刻度显示网格线,major:主(大)刻度;minor:次(小)刻度;both:两者同时显示 axis 可选项,在横/竖轴显示网格线,x:x 轴;y:y 轴;both:两者同时显示 **kwargs 其他 Line2D 属性,常见 Line2D 属性见下表 Line2D 属性用法:grid(color='r', linestyle='-', linewidth=2),部分常见 Line2D 属性如下: 属性 描述 alpha 网格透明度,float 类型,取值范围:[0, 1],默认为 1.0,即不透明 antialiased / aa 是否使用抗锯齿渲染,默认为 True color / c 网格颜色,支持英文颜色名称及其简写、十六进制颜色码等,更多颜色示例参见官网 Color Demo linestyle / ls 网格线条样式:'-' or 'solid', '--' or 'dashed', '-.' or 'dashdot' ':' or 'dotted', 'none' or ' ' or '' linewidth / lw 网格线条宽度,float 类型,默认 0.8 应用举例: 12345678910111213import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']a = [-15, -10, -5, 20, 25, 30]b = [-5, -4, -3, 6, 7, 8]plt.title('这是中文标题')plt.xlabel('这是 x 轴标签')plt.ylabel('这是 y 轴标签')plt.grid(axis='x', color='red', linestyle='-.', linewidth=2)plt.plot(a, b)plt.show() 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105828049未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Matplotlib","slug":"Python-数据分析/Matplotlib","permalink":"https://www.itrhx.com/categories/Python-数据分析/Matplotlib/"}],"tags":[{"name":"Matplotlib","slug":"Matplotlib","permalink":"https://www.itrhx.com/tags/Matplotlib/"},{"name":"画布","slug":"画布","permalink":"https://www.itrhx.com/tags/画布/"},{"name":"网格","slug":"网格","permalink":"https://www.itrhx.com/tags/网格/"}]},{"title":"Python 数据分析三剑客之 Matplotlib(一):初识 Matplotlib 与其 Matplotibrc 配置文件","slug":"A68-Matplotlib-01","date":"2020-04-10T12:07:49.148Z","updated":"2020-08-06T03:05:37.600Z","comments":true,"path":"2020/04/10/A68-Matplotlib-01/","link":"","permalink":"https://www.itrhx.com/2020/04/10/A68-Matplotlib-01/","excerpt":"","text":"Matplotlib 系列文章: Python 数据分析三剑客之 Matplotlib(一):初识 Matplotlib 与其 matplotibrc 配置文件 Python 数据分析三剑客之 Matplotlib(二):文本描述 / 中文支持 / 画布 / 网格等基本图像属性 Python 数据分析三剑客之 Matplotlib(三):图例 / LaTeX / 刻度 / 子图 / 补丁等基本图像属性 Python 数据分析三剑客之 Matplotlib(四):线性图的绘制 Python 数据分析三剑客之 Matplotlib(五):散点图的绘制 Python 数据分析三剑客之 Matplotlib(六):直方图 / 柱状图 / 条形图的绘制 Python 数据分析三剑客之 Matplotlib(七):饼状图的绘制 Python 数据分析三剑客之 Matplotlib(八):等高线 / 等值线图的绘制 Python 数据分析三剑客之 Matplotlib(九):极区图 / 极坐标图 / 雷达图的绘制 Python 数据分析三剑客之 Matplotlib(十):3D 图的绘制 Python 数据分析三剑客之 Matplotlib(十一):最热门最常用的 50 个图表【译文】 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105638122未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【1x00】认识 MatplotlibMatplotlib 是建立在 NumPy 数组基础上的多平台数据可视化程序库,用于在 Python 中绘制数组的 2D 图形库,最初被设计用于完善 SciPy 的生态环境,虽然它起源于模仿 Matlab 图形命令,但它独立于 Matlab,可以以 Pythonic 和面向对象的方式使用。虽然 Matplotlib 主要是在纯 Python 中编写的,但它大量使用 NumPy 和其他扩展代码,即使对于大型数组也能提供良好的性能。它与 NumPy 一起使用,提供了一种有效的 Matlab 开源替代方案。 它也可以和图形工具包一起使用,如 PyQt 和 wxPython。Matplotlib 最重要的特性之一就是具有良好的操作系统兼容性和图形显示底层接口兼容性。 【1x01】简单示例123456>>> import matplotlib.pyplot as plt>>> x = range(2, 26, 2) # 数据在 x 轴的位置,是一个可迭代对象>>> y = range(0, 12) # 数据在 y 轴的位置,是一个可迭代对象>>> plt.plot(x, y) # 绘制线形图[<matplotlib.lines.Line2D object at 0x00BA1D18>]>>> plt.show() 【1x02】图像结构 【1x03】三层结构Matplotlib 三层结构:容器层、辅助显示层、图像层 容器层 容器层主要由 Canvas、Figure、Axes 组成。 Canvas 是位于最底层的系统层,在绘图的过程中充当画板的角色,即放置画布(Figure)的工具。 Figure 是 Canvas 上方的第一层,也是需要用户来操作的应用层的第一层,在绘图的过程中充当画布的角色,可以通过 plt.figure() 设置画布的大小和分辨率等 Axes 是应用层的第二层,在绘图的过程中相当于画布上的绘图区的角色,注意与 Axis 的区别,Axis 是坐标轴,包含大小限制、刻度和刻度标签。 注意点: 一个figure(画布)可以包含多个axes(坐标系/绘图区),但是一个 axes 只能属于一个figure。 一个axes(坐标系/绘图区)可以包含多个axis(坐标轴),包含两个即为 2d 坐标系,三个即为 3d 坐标系 。 辅助显示层 辅助显示层为 Axes(绘图区)内的除了根据数据绘制出的图像以外的内容,主要包括 Axes 外观(facecolor)、边框线(spines)、坐标轴(axis)、坐标轴名称(axis label)、坐标轴刻度(tick)、坐标轴刻度标签(tick label)、网格线(grid)、图例(legend)、标题(title)等内容。该层的设置可使图像显示更加直观更加容易被用户理解,但又不会对图像产生实质的影响。 图像层 图像层指 Axes 内通过 plot(线形图)、scatter(散点图)、bar(柱状图)、histogram(直方图)、pie(饼图) 等函数根据数据绘制出的图像。 三者关系总结 Canvas(画板)位于最底层,用户一般接触不到; Figure(画布)建立在 Canvas 之上; Axes(绘图区)建立在Figure之上; 坐标轴(axis)、图例(legend)等辅助显示层以及图像层都是建立在 Axes 之上。 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105638122未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【2x00】了解 matplotlib.rcParamsmatplotlib 使用 matplotlibrc 配置文件来自定义图形的各种默认属性,称之为 rc 配置或 rc 参数。通过 rc 参数可以修改默认的属性,包括窗体大小、每英寸的点数、线条宽度、颜色、样式、坐标轴、坐标和网络属性、文本、字体等。rc 参数存储在字典变量中,通过字典的方式进行访问。 执行 matplotlib.rcParams.keys() 命令可以查看所有的 rc 参数及其默认值;执行 matplotlib.matplotlib_fname() 命令可以查看 matplotlibrc 配置文件在本地的路径。 官网介绍:https://matplotlib.org/tutorials/introductory/customizing.html 配置文件 matplotibrc 主要包括以下配置要素: axes:坐标轴的背景颜色、坐标轴的边缘颜色、刻度线的大小、刻度标签的字体大小等; figure:画布标题大小、画布标题粗细、画布像素(dpi)、 画布背景颜色和边缘颜色等; font:字体类别、字体风格、字体粗细和字体大小等; grid:网格颜色、网格线条风格、网格线条宽度和网格透明度; legend:图例的文本大小、阴影、图例线框风格等; lines:设置线条属性,包括颜色、线条风格、线条宽度和标记风格等; patch:填充 2D 空间的图形对象,包括多边形和圆; savefig:保存画布图像的分辨率、背景颜色和边缘颜色等; text:文本颜色、LaTex 渲染文本等; xtick / ytick:x 轴和 y 轴的主次要刻度线的大小、宽度、刻度线颜色和刻度标签大小等。 我们可以在 Python 项目中动态设置 rc 参数,所有 rc 参数设置都存储在名为 matplotlib.rcParams 的类似于字典的变量中,该变量对于 Matplotlib 软件包是全局的。rcParams 可以直接修改。通过这种方法的修改会对全局产生影响,在 Matplotlib 的其他方法中也可以单独对某个参数进行修改,后续介绍不同方法时会见到。 rcParams 修改示例: 123456789101112131415161718192021import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei'] # 定义全局字体plt.rcParams['xtick.color'] = 'red' # 定义 x 轴刻度颜色plt.rcParams['lines.marker'] = 'o' # 定义线条上点的形状plt.rcParams['legend.loc'] = 'upper left' # 定义图例在左上角x = range(2, 26, 2)y = range(0, 12)a = [5, 10, 15, 20, 25, 30]b = [3, 4, 5, 6, 7, 8]plt.title('This is a title / 这是标题')plt.xlabel('这是 x 轴标题')plt.ylabel('这是 y 轴标题')plt.grid(True)plt.plot(x, y)plt.plot(a, b)plt.legend(['图例一', '图例二'])plt.show() 【2x01】axes 部分属性 属性及其默认值 描述 其他取值 mpl.rcParams[‘axes.axisbelow‘] = ‘line’ 网格线和刻度的位置 line:在画板上方,在线条下方False:在线条和画板的上方True:在画板下方 mpl.rcParams[‘axes.edgecolor‘] = ‘black’ 轴边缘颜色 其他颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo mpl.rcParams[‘axes.facecolor‘] = ‘white’ 轴背景色 其他颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo mpl.rcParams[‘axes.labelcolor‘] = ‘black’ 轴标题颜色 其他颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo mpl.rcParams[‘axes.grid‘] = False 是否显示网格 False:不显示网格;True:显示网格 mpl.rcParams[‘axes.grid.axis‘] = ‘both’ 网格应用于哪个轴 x:x 轴;y:y 轴;both:同时应用于两个轴 mpl.rcParams[‘axes.grid.which‘] = ‘major’ 网格应用于哪个刻度 major:主(大)刻度;minor:次(小刻度);both:同时应用于两个刻度 mpl.rcParams[‘axes.labelpad‘] = 4.0 轴标题和轴之间的间距 float 类型间距值 mpl.rcParams[‘axes.labelsize‘] = ‘medium’ x 轴和 y 轴标题的字体大小 xx-small, x-small, small, medium large, x-large, xx-large, smaller, larger也可以使用数字来表示字体大小 mpl.rcParams[‘axes.labelweight‘] = ‘normal’ x 轴和 y 轴标题的字体粗细 normal:正常粗细;bold:粗体;light:细体数字值 400 等价于 normal,700 等价于 bold mpl.rcParams[‘axes.linewidth‘] = 0.8 轴边线宽度 float 类型宽度值 mpl.rcParams[‘axes.titlecolor‘] = ‘auto’ 图表标题颜色 默认取 text.color 的值其他颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo mpl.rcParams[‘axes.titlelocation‘] = ‘center’ 图表标题位置 left:左;right:右;center:中间 mpl.rcParams[‘axes.titlepad‘] = 6.0 图表标题和轴之间的间距 float 类型间距值 mpl.rcParams[‘axes.titlesize‘] = ‘large’ 图表标题字体大小 xx-small, x-small, small, medium large, x-large, xx-large, smaller, larger也可以使用数字来表示字体大小 mpl.rcParams[‘axes.titleweight‘] = ‘normal’ 图表标题字体粗细 normal:正常粗细;bold:粗体;light:细体数字值 400 等价于 normal,700 等价于 bold mpl.rcParams[‘axes.xmargin‘] = 0.05 x 轴边距 取值范围 [0, 1] mpl.rcParams[‘axes.ymargin‘] = 0.05 y 轴边距 取值范围 [0, 1] mpl.rcParams[‘axes.unicode_minus‘] = True 对负号使用 Unicode 而不是连字符 True:是;False:否 mpl.rcParams[‘axes3d.grid‘] = True 是否在三维轴上显示网格 True:是;False:否 【2x02】figure 部分属性 属性及其默认值 描述 其他取值 mpl.rcParams[‘figure.dpi‘] = 100 画布像素(dpi) float 类型像素值 mpl.rcParams[‘figure.edgecolor‘] = ‘white’ 画布边缘颜色 其他颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo mpl.rcParams[‘figure.facecolor‘] = ‘white’ 画布背景颜色 其他颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo mpl.rcParams[‘figure.figsize‘] = [6.4, 4.8] 画布尺寸 [长, 宽] float 类型尺寸值(英寸) mpl.rcParams[‘figure.frameon‘] = True 是否启用图框 True:是;False:否 mpl.rcParams[‘figure.titlesize‘] = ‘large’ 画布标题大小 xx-small, x-small, small, medium large, x-large, xx-large, smaller, larger也可以使用数字来表示字体大小 mpl.rcParams[‘figure.titleweight‘] = ‘normal’ 画布标题粗细 normal:正常粗细;bold:粗体;light:细体数字值 400 等价于 normal,700 等价于 bold 【2x03】font 部分属性 属性及其默认值 描述 其他取值 mpl.rcParams[‘font.family‘] = [‘sans-serif’] 规定字体系列 字体名称 mpl.rcParams[‘font.sans-serif‘] = [‘DejaVu Sans, ……’] 定义无衬线字体 默认是一些西文字体,可将其设置成其他字体来显示中文 mpl.rcParams[‘font.serif‘] = [‘DejaVu Sans, ……’] 定义有衬线字体 默认是一些西文字体,可将其设置成其他字体来显示中文 mpl.rcParams[‘font.size‘] = 10.0 定义字体大小 float 数字类型字体大小 mpl.rcParams[‘font.weight‘] = ‘normal’ 定义字体粗细 normal:正常粗细;bold:粗体;light:细体数字值 400 等价于 normal,700 等价于 bold 【2x04】grid 部分属性 属性及其默认值 描述 其他取值 mpl.rcParams[‘grid.alpha‘] = 1.0 网格透明度 float 类型,取值范围:[0, 1] mpl.rcParams[‘grid.color‘] = ‘#b0b0b0’ 网格颜色 其他颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo mpl.rcParams[‘grid.linestyle‘] = ‘-‘ 网格线的样式 '-' or 'solid', '--' or 'dashed', '-.' or 'dashdot' ':' or 'dotted', 'none' or ' ' or '' mpl.rcParams[‘grid.linewidth‘] = 0.8 网格宽度 float 类型宽度值 【2x05】legend 部分属性 属性及其默认值 描述 其他取值 mpl.rcParams[‘legend.borderaxespad‘] = 0.5 图例距离轴之间的距离 float 类型距离值 mpl.rcParams[‘legend.borderpad‘] = 0.4 图例边框空白区域大小 float 类型大小值 mpl.rcParams[‘legend.columnspacing‘] = 2.0 图例列间距 float 类型距离值 mpl.rcParams[‘legend.edgecolor‘] = 0.8 图例边缘线颜色 其他颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo mpl.rcParams[‘legend.facecolor‘] = ‘inherit’ 图例背景颜色 默认继承自 axes.facecolor其他颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo mpl.rcParams[‘legend.fancybox‘] = True 是否使用圆形框作为图例背景 True:使用圆形框;False:使用矩形框 mpl.rcParams[‘legend.fontsize‘] = ‘medium’ 图例字体大小 xx-small, x-small, small, medium large, x-large, xx-large, smaller, larger也可以使用数字来表示字体大小 mpl.rcParams[‘legend.framealpha‘] = 0.8 图例透明度 float 类型,取值范围:[0, 1] mpl.rcParams[‘legend.frameon‘] = True 是否在画布之上绘制图例 True:是;False:否 mpl.rcParams[‘legend.handleheight‘] = 0.7 图例的高度 float 类型高度值 mpl.rcParams[‘legend.handlelength‘] = 2.0 图例的宽度 float 类型宽度值 mpl.rcParams[‘legend.handletextpad‘] = 0.8 图例和图例文本之间的水平距离 float 类型距离值 mpl.rcParams[‘legend.labelspacing‘] = 0.5 不同图例之间的垂直距离 float 类型距离值 mpl.rcParams[‘legend.loc‘] = ‘best’ 图例在画布中的位置 best, upper right, upper left, lower left lower right, right, center left, center right lower center, upper center, center mpl.rcParams[‘legend.shadow‘] = False 是否给图例添加阴影效果 True:是;False:否 【2x06】lines 部分属性 属性及其默认值 描述 其他取值 mpl.rcParams[‘lines.antialiased‘] = True 是否以抗锯齿方式渲染线条 True:是;False:否 mpl.rcParams[‘lines.color‘] = ‘C0’ 线条颜色(对 plot() 没有影响) 其他颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo mpl.rcParams[‘lines.linestyle‘] = ‘-‘ 线条样式 '-', '--', '-.', ':', 'solid', 'dashed', 'dashdot', 'dotted', 'none', ' ', '' mpl.rcParams[‘lines.linewidth‘] = 1.5 线条宽度 float 类型宽度值 mpl.rcParams[‘lines.marker‘] = ‘None’ 线条上点的形状 ., ,, o, v, ^ 等,具体常见 matplotlib.markers mpl.rcParams[‘lines.markeredgecolor‘] = ‘auto’ 线条上点边缘的颜色 其他颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo mpl.rcParams[‘lines.markerfacecolor‘] = ‘auto’ 线条上点的颜色 其他颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo mpl.rcParams[‘lines.markeredgewidth‘] = 1.0 线条上点的粗细 float 类型粗细值 mpl.rcParams[‘lines.markersize‘] = 6.0 线条上点的大小 float 类型大小值 【2x07】patch 部分属性 属性及其默认值 描述 其他取值 mpl.rcParams[‘patch.antialiased‘] = True 以抗锯齿方式渲染补丁 True:是;False:否 mpl.rcParams[‘patch.edgecolor‘] = ‘black’ 补丁边缘颜色 其他颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo mpl.rcParams[‘patch.facecolor‘] = ‘C0’ 补丁颜色 其他颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo mpl.rcParams[‘patch.linewidth‘] = 1.0 补丁边缘宽度(以磅为单位) float 类型宽度值 【2x08】savefig 部分属性 属性及其默认值 描述 其他取值 mpl.rcParams[‘savefig.bbox‘] = None 是否以紧凑形式保存图片 standard:标准形式;tight:紧凑形式(去掉边上多余的空白) mpl.rcParams[‘savefig.pad_inches‘] = 0.1 savefig.bbox 参数为 tight 时,图片使用的填充值(相当于 html 中的 Padding) float 类型填充值 mpl.rcParams[‘savefig.dpi‘] = ‘figure’ 保存图片的像素(dpi) str 类型像素值 mpl.rcParams[‘savefig.edgecolor‘] = ‘white’ 保存图片的边缘颜色 其他颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo mpl.rcParams[‘savefig.facecolor‘] = ‘white’ 保存图片的画布颜色 其他颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo mpl.rcParams[‘savefig.format‘] = ‘png’ 保存图片的格式 eps, pdf, pgf, png, ps, raw, rgba, svg, svgz mpl.rcParams[‘savefig.transparent‘] = False 保存图片的背景是否透明 True:是;False:否 【2x09】text 部分属性 属性及其默认值 描述 其他取值 mpl.rcParams[‘text.antialiased‘] = True 是否以抗锯齿方式渲染文本 True:是;False:否 mpl.rcParams[‘text.color‘] = ‘red’ 文本颜色 其他颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo mpl.rcParams[‘text.usetex‘] = False 是否使用 LaTeX 排版系统(主要用于生成复杂表格和数学公式) True:是;False:否 【2x10】xtick 部分属性 属性及其默认值 描述 其他取值 mpl.rcParams[‘xtick.color‘] = ‘black’ x 轴刻度的颜色 其他颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo mpl.rcParams[‘xtick.direction‘] = ‘out’ x 轴刻度的方向 in:内部(x 轴上方);out:外部(x 轴下方)inout:同时在内部和外部 mpl.rcParams[‘xtick.bottom‘] = True 是否在画布底部显示 x 轴刻度 True:是;False:否 mpl.rcParams[‘xtick.top‘] = False 是否在画布顶部显示 x 轴刻度 True:是;False:否 mpl.rcParams[‘xtick.labelbottom‘] = True 是否在画布底部显示 x 轴刻度文字标签 True:是;False:否 mpl.rcParams[‘xtick.labeltop‘] = False 是否在画布顶部显示 x 轴刻度文字标签 True:是;False:否 mpl.rcParams[‘xtick.labelsize‘] = ‘medium’ x 轴刻度文字大小 xx-small, x-small, small, medium large, x-large, xx-large, smaller, larger也可以使用数字来表示字体大小 mpl.rcParams[‘xtick.major.bottom‘] = True 是否在画布底部显示 x 轴主(大)刻度 True:是;False:否 mpl.rcParams[‘xtick.major.top‘] = True 是否在画布顶部显示 x 轴主(大)刻度 True:是;False:否 mpl.rcParams[‘xtick.major.pad‘] = 3.5 x 轴主(大)刻度与文字标签的距离 float 类型距离值 mpl.rcParams[‘xtick.major.size‘] = 3.5 x 轴主(大)刻度的大小 float 类型大小值 mpl.rcParams[‘xtick.major.width‘] = 0.8 x 轴主(大)刻度的宽度 float 类型宽度值 mpl.rcParams[‘xtick.minor.bottom‘] = True 是否在画布底部显示 x 轴次(小)刻度 True:是;False:否 mpl.rcParams[‘xtick.minor.top‘] = True 是否在画布顶部显示 x 轴次(小)刻度 True:是;False:否 mpl.rcParams[‘xtick.minor.pad‘] = 3.4 x 轴次(小)刻度与文字标签的距离 float 类型距离值 mpl.rcParams[‘xtick.minor.size‘] = 2.0 x 轴次(小)刻度的大小 float 类型大小值 mpl.rcParams[‘xtick.minor.width‘] = 0.6 x 轴次(小)刻度的宽度 float 类型宽度值 mpl.rcParams[‘xtick.minor.visible‘] = False x 轴次(小)刻度的可见性 True:是;False:否 【2x11】ytick 部分属性 属性及其默认值 描述 其他取值 mpl.rcParams[‘ytick.color‘] = ‘black’ y 轴刻度的颜色 其他颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo mpl.rcParams[‘ytick.direction‘] = ‘out’ y 轴刻度的方向 in:内部(y 轴右方);out:外部(y 轴左方)inout:同时在内部和外部 mpl.rcParams[‘ytick.left‘] = True 是否在画布左边显示 y 轴刻度 True:是;False:否 mpl.rcParams[‘ytick.right‘] = False 是否在画布右边显示 y 轴刻度 True:是;False:否 mpl.rcParams[‘ytick.labelleft‘] = True 是否在画布左边显示 y 轴刻度文字标签 True:是;False:否 mpl.rcParams[‘ytick.labelright‘] = False 是否在画布右边显示 y 轴刻度文字标签 True:是;False:否 mpl.rcParams[‘ytick.labelsize‘] = ‘medium’ y 轴刻度文字大小 xx-small, x-small, small, medium large, x-large, xx-large, smaller, larger也可以使用数字来表示字体大小 mpl.rcParams[‘ytick.major.left‘] = True 是否在画布左边显示 y 轴主(大)刻度 True:是;False:否 mpl.rcParams[‘ytick.major.right‘] = True 是否在画布右边显示 y 轴主(大)刻度 True:是;False:否 mpl.rcParams[‘ytick.major.pad‘] = 3.5 y 轴主(大)刻度与文字标签的距离 float 类型距离值 mpl.rcParams[‘ytick.major.size‘] = 3.5 y 轴主(大)刻度的大小 float 类型大小值 mpl.rcParams[‘ytick.major.width‘] = 0.8 y 轴主(大)刻度的宽度 float 类型宽度值 mpl.rcParams[‘ytick.minor.left‘] = True 是否在画布左边显示 y 轴次(小)刻度 True:是;False:否 mpl.rcParams[‘ytick.minor.right‘] = True 是否在画布右边显示 y 轴次(小)刻度 True:是;False:否 mpl.rcParams[‘ytick.minor.pad‘] = 3.4 y 轴次(小)刻度与文字标签的距离 float 类型距离值 mpl.rcParams[‘ytick.minor.size‘] = 2.0 y 轴次(小)刻度的大小 float 类型大小值 mpl.rcParams[‘ytick.minor.width‘] = 0.6 y 轴次(小)刻度的宽度 float 类型宽度值 mpl.rcParams[‘ytick.minor.visible‘] = False y 轴次(小)刻度的可见性 True:是;False:否 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105638122未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Matplotlib","slug":"Python-数据分析/Matplotlib","permalink":"https://www.itrhx.com/categories/Python-数据分析/Matplotlib/"}],"tags":[{"name":"Matplotlib","slug":"Matplotlib","permalink":"https://www.itrhx.com/tags/Matplotlib/"},{"name":"matplotibrc","slug":"matplotibrc","permalink":"https://www.itrhx.com/tags/matplotibrc/"}]},{"title":"Python 数据分析三剑客之 NumPy(六):矩阵 / 线性代数库与 IO 操作","slug":"A67-NumPy-06","date":"2020-03-30T07:23:44.601Z","updated":"2020-07-06T13:28:17.565Z","comments":true,"path":"2020/03/30/A67-NumPy-06/","link":"","permalink":"https://www.itrhx.com/2020/03/30/A67-NumPy-06/","excerpt":"","text":"NumPy 系列文章: Python 数据分析三剑客之 NumPy(一):理解 NumPy / 数组基础 Python 数据分析三剑客之 NumPy(二):数组索引 / 切片 / 广播 / 拼接 / 分割 Python 数据分析三剑客之 NumPy(三):数组的迭代与位运算 Python 数据分析三剑客之 NumPy(四):字符串函数总结与对比 Python 数据分析三剑客之 NumPy(五):数学 / 算术 / 统计 / 排序 / 条件 / 判断函数合集 Python 数据分析三剑客之 NumPy(六):矩阵 / 线性代数库与 IO 操作 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105511641未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【1x00】NumPy 矩阵库numpy.matlib 模块是 NumPy 的矩阵库,该矩阵库包含多种函数,函数返回的是一个矩阵,而不是 Ndarray 对象。 官方文档介绍:https://numpy.org/doc/1.18/reference/routines.matlib.html 【1x01】numpy.mat()numpy.mat() 函数将输入数组转换为为矩阵。 基本语法:numpy.mat(data[, dtype=None]) 参数 描述 data 输入数据,如果 data 为字符串,则需要用逗号或空格分隔列,用分号分隔行 dtype 输出矩阵的数据类型,可选项 应用举例: 12345678>>> import numpy as np>>> a = np.mat([1, 2, 3])>>> amatrix([[1, 2, 3]])>>> a[0]matrix([[1, 2, 3]])>>> a[0,1]2 123456789>>> import numpy as np>>> a = np.array([[1, 2], [3, 4]])>>> aarray([[1, 2], [3, 4]])>>> b = np.mat(a)>>> bmatrix([[1, 2], [3, 4]]) 【1x02】numpy.asmatrix()numpy.asmatrix() 函数将输入数组转换为为矩阵。 基本语法:numpy.asmatrix(data[, dtype=None]) 参数 描述 data 输入数据,如果 data 为字符串,则需要用逗号或空格分隔列,用分号分隔行 dtype 输出矩阵的数据类型,可选项 应用举例: 12345>>> import numpy as np>>> a = np.array([[1, 2], [3, 4]])>>> np.asmatrix(a)matrix([[1, 2], [3, 4]]) 【1x03】numpy.matrix()numpy.matrix() 函数从类似数组的对象或数据字符串中返回一个矩阵。 注意:此函数已经不建议再使用,在未来的版本当中可能会被删除。 基本语法:class numpy.matrix(data[, dtype=None, copy=True]) 参数 描述 data 数组或者字符串,如果 data 为字符串,则需要用逗号或空格分隔列,用分号分隔行 dtype 输出矩阵的数据类型,可选项 copy 是否复制数据到一个新矩阵,可选项 应用举例: 12345678910>>> import numpy as np>>> a = np.matrix('1 2; 3 4')>>> amatrix([[1, 2], [3, 4]])>>> >>> b = np.matrix([[1, 2], [3, 4]])>>> bmatrix([[1, 2], [3, 4]]) 【1x04】mat() / asmatrix() / matrix() 的区别如果输入已经是一个矩阵或一个数组,则 mat() 和 asmatrix() 函数不会执行复制操作,相当于 matrix(data, copy=False) 对比举例: 123456789101112131415161718192021222324252627282930313233>>> import numpy as np>>> a = np.array([[1, 2], [3, 4]])>>> b = np.mat(a)>>> c = np.matrix(a)>>> d = np.asmatrix(a)>>> >>> aarray([[1, 2], [3, 4]])>>> bmatrix([[1, 2], [3, 4]])>>> cmatrix([[1, 2], [3, 4]])>>> dmatrix([[1, 2], [3, 4]])>>> >>> a[1][1] = 0>>> >>> aarray([[1, 2], [3, 0]])>>> bmatrix([[1, 2], [3, 0]])>>> c # matrix() 函数默认执行 copy 操作,所以数据不变matrix([[1, 2], [3, 4]])>>> dmatrix([[1, 2], [3, 0]]) 【1x05】numpy.bmat()numpy.bmat() 函数用于从字符串、嵌套序列或数组生成矩阵对象,一般用于创建复合矩阵。 基本语法:numpy.bmat(obj[, ldict=None, gdict=None]) 参数 描述 obj 数组或者字符串,如果 data 为字符串,则需要用逗号或空格分隔列,用分号分隔行 ldict 字典,用于替换当前帧中的本地操作数。如果 obj 不是字符串或 gdict 为 None,则将被忽略 gdict 字典,用于替换当前帧中的全局操作数。如果 obj 不是字符串则忽略 应用举例: 123456789101112131415161718192021>>> import numpy as np>>> a = np.mat('1 1; 1 1')>>> b = np.mat('2 2; 2 2')>>> c = np.mat('3 4; 5 6')>>> d = np.mat('7 8; 9 0')>>> >>> np.bmat([[a, b], [c, d]])matrix([[1, 1, 2, 2], [1, 1, 2, 2], [3, 4, 7, 8], [5, 6, 9, 0]])>>> np.bmat(np.r_[np.c_[a, b], np.c_[c, d]])matrix([[1, 1, 2, 2], [1, 1, 2, 2], [3, 4, 7, 8], [5, 6, 9, 0]])>>> np.bmat('a,b; c,d')matrix([[1, 1, 2, 2], [1, 1, 2, 2], [3, 4, 7, 8], [5, 6, 9, 0]]) 【1x06】numpy.matlib.empty()numpy.matlib.empty() 函数用于创建一个给定形状和数据类型的新矩阵。 基本语法:numpy.matlib.empty(shape[, dtype=None, order='C']) 参数 描述 shape 定义新矩阵的形状 dtype 数据类型,可选项 order 以行优先(C)或列优先(Fortran)的顺序存储多维数据在内存中,可选项 应用举例: 1234567>>> import numpy as np>>> print(np.matlib.empty((2, 2)))[[9.90263869e+067 8.01304531e+262] [2.60799828e-310 0.00000000e+000]]>>> print(np.matlib.empty((2, 2), dtype=int))[[ -793016358 -243407933] [ -959331519 -2060787213]] 【1x07】numpy.matlib.zeros()numpy.matlib.zeros() 函数创建一个以 0 填充的给定形状和类数据型的矩阵。 基本语法:numpy.matlib.zeros(shape[, dtype=None, order='C']) 参数 描述 shape 定义新矩阵的形状 dtype 数据类型,可选项 order 以行优先(C)或列优先(Fortran)的顺序存储多维数据在内存中,可选项 应用举例: 1234>>> import numpy as np>>> np.matlib.zeros((2, 3))matrix([[0., 0., 0.], [0., 0., 0.]]) 【1x08】numpy.matlib.ones()numpy.matlib.ones() 函数创建一个以 1 填充的给定形状和类数据型的矩阵。 基本语法:numpy.matlib.ones(shape[, dtype=None, order='C']) 参数 描述 shape 定义新矩阵的形状 dtype 数据类型,可选项 order 以行优先(C)或列优先(Fortran)的顺序存储多维数据在内存中,可选项 应用举例: 1234>>> import numpy as np>>> np.matlib.ones((2, 3))matrix([[1., 1., 1.], [1., 1., 1.]]) 【1x09】numpy.matlib.eye()numpy.matlib.eye() 函数创建一个对角线元素为 1,其他位置为零的矩阵。 基本语法:numpy.matlib.eye(n[, M=None, k=0, dtype=<class 'float'>, order='C']) 参数 描述 n 返回的矩阵的行数,int 类型 M 返回的矩阵的列数,int 类型,可选项,默认为 n k 对角线索引,可选项,0 表示主对角线,正值表示上对角线,负值表示下对角线,该对角线上元素的值将会是 1 dtype 返回矩阵的数据类型,可选项 order 以行优先(C)或列优先(Fortran)的顺序存储多维数据在内存中,可选项 应用举例: 12345678910111213>>> import numpy as np>>> print(np.matlib.eye(n=3, k=1))[[0. 1. 0.] [0. 0. 1.] [0. 0. 0.]]>>> print(np.matlib.eye(n=3, k=-1))[[0. 0. 0.] [1. 0. 0.] [0. 1. 0.]]>>> print(np.matlib.eye(n=3, M=4, k=0, dtype=int))[[1 0 0 0] [0 1 0 0] [0 0 1 0]] 【1x10】numpy.matlib.identity()numpy.matlib.identity() 函数创建一个给定大小的单位矩阵。 单位矩阵:在矩阵的乘法中,有一种矩阵起着特殊的作用,如同数的乘法中的1,这种矩阵被称为单位矩阵。它是个方阵,从左上角到右下角的对角线(称为主对角线)上的元素均为1。除此以外全都为0。 基本语法:numpy.matlib.identity(n[, dtype=None]) 参数 描述 n 返回的单位矩阵的大小,int 类型 dtype 可选项,返回的单位矩阵的数据类型 应用举例: 12345>>> import numpy as np>>> print(np.matlib.identity(3, dtype=int))[[1 0 0] [0 1 0] [0 0 1]] 【1x11】numpy.matlib.repmat()numpy.matlib.repmat() 函数用于重复数组或矩阵 m*n 次。 基本语法:numpy.matlib.repmat(a, m, n) 参数 描述 a 待处理的数组对象 m,n 沿第一轴和第二轴重复的次数 应用举例: 1234567891011121314>>> import numpy as np>>> a = np.array(1)>>> b = np.arange(4)>>> aarray(1)>>> barray([0, 1, 2, 3])>>> >>> print(np.matlib.repmat(a, 2, 3))[[1 1 1] [1 1 1]]>>> print(np.matlib.repmat(b, 2, 2))[[0 1 2 3 0 1 2 3] [0 1 2 3 0 1 2 3]] 【1x12】numpy.matlib.rand()numpy.matlib.rand() 函数创建一个给定大小的矩阵,其中的数据在 [0, 1) 区间随机取值来填充。 基本语法:numpy.matlib.rand(*args) 参数解释:*args:输出矩阵的形状,如果给定为 N 个整数,则每个整数指定一维的大小,如果以元组形式给出,则该元组表示输出矩阵完整的形状。 应用举例: 123456789101112>>> import numpy as np>>> print(np.matlib.rand(2, 3))[[0.27957871 0.48748368 0.0970184 ] [0.71062224 0.92503824 0.72415015]]>>>>>> print(np.matlib.rand((2, 3)))[[0.08814715 0.0307317 0.77775332] [0.81158748 0.09173265 0.77497881]]>>>>>> print(np.matlib.rand(2, 3), 4) # 如果第一个参数是元组,则其他参数将被忽略[[0.53407924 0.56006372 0.63903716] [0.56132381 0.90300814 0.44074964]] 4 【1x13】numpy.matlib.randn()numpy.matlib.randn() 函数创建一个标准正态分布的随机矩阵。 标准正态分布,是一个在数学、物理及工程等领域都非常重要的概率分布,在统计学的许多方面有着重大的影响力。期望值μ=0,即曲线图象对称轴为Y轴,标准差 σ=1 条件下的正态分布,记为 N(0,1)。 标准正态分布又称为 u 分布,是以 0 为均数、以 1 为标准差的正态分布,记为 N(0,1) 基本语法:numpy.matlib.randn(*args) 参数解释:*args:输出矩阵的形状,如果给定为 N 个整数,则每个整数指定一维的大小,如果以元组形式给出,则该元组表示输出矩阵完整的形状。 应用举例: 12345678>>> import numpy as np>>> print(np.matlib.randn(2, 3))[[ 0.82976978 -0.9798698 0.71262414] [ 2.31211127 -0.5090537 1.12357032]]>>> >>> print(2.5 * np.matlib.randn((2, 4)) + 3) # 2 x 4 矩阵 N(3, 6.25)[[-0.66974538 4.9354863 2.46138048 7.05576713] [ 0.80688217 1.79017491 3.78979646 -1.99071372]] 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105511641未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【2x00】NumPy 线性代数库线性代数是数学的一个分支,它的研究对象是向量,向量空间(或称线性空间),线性变换和有限维的线性方程组。NumPy 中也提供了线性代数函数库 numpy.linalg。 官方文档介绍:https://numpy.org/doc/1.18/reference/routines.linalg.html 【2x01】numpy.dot()numpy.dot() 函数用于计算两个数组的点积。 基本语法:numpy.dot(a, b[, out=None]) 参数 描述 a 第一个数组 b 第二个数组 out 可选项,放置结果的备用输出数组 如果 a 和 b 均为一维数组,计算的是这两个数组对应下标元素的乘积和(数学上称之为内积); 如果 a 和 b 均为二维数组,计算的是两个数组的矩阵乘积; 如果 a 和 b 均为多维数组,它的通用计算公式为:dot(a, b)[i,j,k,m] = sum(a[i,j,:] * b[k,:,m]),即结果数组中的每个元素都是数组 a 的最后一维上的所有元素与数组 b 的倒数第二维上的所有元素的乘积和。 应用举例: 12345678910111213>>> import numpy as np>>> a = np.array([[1,2],[3,4]])>>> b = np.array([[11,12],[13,14]])>>> print(np.dot(a,b)) # [[1*11+2*13, 1*12+2*14],[3*11+4*13, 3*12+4*14]][[37 40] [85 92]]>>> >>> c = np.arange(3*4*5*6).reshape((3,4,5,6))>>> d = np.arange(3*4*5*6)[::-1].reshape((5,4,6,3))>>> print(np.dot(c, d)[2,3,2,1,2,2])499128>>> print(sum(c[2,3,2,:] * d[1,2,:,2]))499128 【2x02】numpy.vdot()numpy.vdot() 函数返回两个向量的点积,如果第一个参数是复数,那么它的共轭复数会用于计算。 如果参数是多维数组,它会被展开。 共轭复数:两个实部相等,虚部互为相反数的复数互为共轭复数。 基本语法:numpy.vdot(a, b) 应用举例: 12345678910111213>>> import numpy as np>>> a = np.array([1+2j, 3+4j])>>> b = np.array([5+6j, 7+8j])>>> print(np.vdot(a, b)) # a 的共轭复数用于计算:(1-2j) * (5+6j) + (3-4j) * (7+8j)(70-8j)>>> print(np.vdot(b, a)) # b 的共轭复数用于计算:(1+2j) * (5-6j) + (3+4j) * (7-8j)(70+8j)>>> >>> >>> c = np.array([[1, 4], [5, 6]])>>> d = np.array([[4, 1], [2, 2]])>>> print(np.vdot(c, d)) # 1*4 + 4*1 + 5*2 + 6*230 【2x03】numpy.inner()numpy.inner() 函数计算一维数组的点积,对于其他维度,返回最后一个轴上的和的乘积。 基本语法:numpy.inner(a, b) 应用举例: 123456789101112>>> import numpy as np>>> a = np.array([[1,2],[3,4]])>>> b = np.array([[11,12],[13,14]])>>> print(np.inner(a,b)) # [[1*11+2*12, 1*13+2*14], [3*11+4*12, 3*13+4*14]][[35 41] [81 95]]>>> >>> >>> c = np.array([1,2,3])>>> d = np.array([0,1,0])>>> print(np.inner(c,d)) # 1*0+2*1+3*02 【2x04】numpy.outer()numpy.outer() 函数计算两个向量的外积。 基本语法:numpy.outer(a, b[, out=None]) 参数 描述 a 第一个向量,如果不是一维的则在计算前会将其展平 b 第一个向量,如果不是一维的则在计算前会将其展平 out 结果存储的位置,可选项,类似于 (M, N) 结构的 Ndarray 对象 外积一般指两个向量的向量积,若两向量:a = [a0, a1, ..., aM] b = [b0, b1, ..., bN],外积如下: 1234[[a0*b0 a0*b1 ... a0*bN ] [a1*b0 . [ ... . [aM*b0 aM*bN ]] 应用举例: 1234567891011121314>>> import numpy as np>>> a = np.array([1, 2, 3, 4])>>> b = np.array([5, 6, 7, 8])>>> print(np.outer(a, b))[[ 5 6 7 8] [10 12 14 16] [15 18 21 24] [20 24 28 32]]>>> >>> c = np.array(['a', 'b', 'c'], dtype=object)>>> print(np.outer(c, [1, 2, 3]))[['a' 'aa' 'aaa'] ['b' 'bb' 'bbb'] ['c' 'cc' 'ccc']] 【2x05】numpy.matmul()numpy.matmul() 函数计算两个矩阵的乘积。 矩阵的乘积运算: 设 A 为 m x p 的矩阵,B 为 p x n 的矩阵,那么称 m x n 的矩阵 C 为矩阵 A 与 B 的乘积,记作 C = AB,其中矩阵 C 中的第 i 行第 j 列元素可以表示为: $$ (AB){ij} = \\sum{k=1}^p a_{ik}b_{kj} = a_{i1}b_{1j} + a_{i2}b_{2j} + … + a_{ip}b_{pj} $$ $$A =\\left[\\begin{matrix}a_{1,1} & a_{1,2} & a_{1,3} \\a_{2,1} & a_{2,2} & a_{2,3}\\end{matrix}\\right]\\qquad\\qquad\\qquad\\qquad\\qquad\\qquad B =\\left[\\begin{matrix}b_{1,1} & b_{1,2} \\b_{2,1} & b_{2,2} \\b_{3,1} & b_{3,2}\\end{matrix}\\right]$$ $$C = AB =\\left[\\begin{matrix}a_{1,1}b_{1,1} & a_{1,2}b_{2,1} & a_{1,3}b_{3,1}, & a_{1,1}b_{1,2} & a_{1,2}b_{2,2} & a_{1,3}b_{3,2} \\a_{2,1}b_{1,1} & a_{2,2}b_{2,1} & a_{2,3}b_{3,1}, & a_{2,1}b_{1,2} & a_{2,2}b_{2,2} & a_{2,3}b_{3,2}\\end{matrix}\\right]$$ 矩阵相乘的条件: 当矩阵 A 的列数(column)等于矩阵 B 的行数(row)时,A 与 B 可以相乘; 矩阵 C 的行数等于矩阵 A 的行数,C 的列数等于 B 的列数; 乘积 C 的第 m 行第 n 列的元素等于矩阵 A 的第 m 行的元素与矩阵 B 的第 n 列对应元素乘积之和。 应用举例: 12345678910111213141516171819202122>>> import numpy as np>>> a = np.array([[1,0], [0,1]])>>> b = np.array([[4,1], [2,2]])>>> print(np.matmul(a, b))[[4 1] [2 2]]>>> >>> c = np.array([[1,0], [0,1]])>>> d = np.array([1,2])>>> print(np.matmul(c, d))[1 2]>>> print(np.matmul(d, c))[1 2]>>> >>> e = np.arange(8).reshape(2,2,2)>>> f = np.arange(4).reshape(2,2)>>> print(np.matmul(e, f))[[[ 2 3] [ 6 11]] [[10 19] [14 27]]] 【2x06】numpy.tensordot()numpy.tensordot() 函数计算两个不同维度矩阵的乘积。 基本语法:numpy.tensordot(a, b, axes=2) 参数 描述 a 第一个矩阵 b 第二个矩阵 axis 指定收缩的轴如果是一个整型 m,表示指定数组 a 的后 m 个轴和数组 b 的前 m 个轴分别进行内积,即对应位置元素相乘、再整体求和如果是一个列表 [m, n],那么表示 a 的第 m+1 个 (索引为m) 轴和 b 的第 n+1 (索引为n) 个轴进行内积 应用举例: 123456789101112131415161718192021222324252627282930>>> import numpy as np>>> a = np.random.randint(0, 9, (3, 4))>>> b = np.random.randint(0, 9, (4, 5))>>> aarray([[4, 0, 3, 6], [1, 3, 2, 2], [6, 1, 3, 4]])>>> barray([[1, 0, 0, 7, 6], [3, 8, 7, 5, 0], [4, 7, 0, 8, 0], [3, 8, 5, 0, 1]])>>> print(np.tensordot(a, b, 1))[[34 69 30 52 30] [24 54 31 38 8] [33 61 27 71 40]]>>>>>> c = np.array(range(1, 9)).reshape(2, 2, 2)>>> d = np.array(('a', 'b', 'c', 'd'), dtype=object).reshape(2, 2)>>> carray([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])>>> darray([['a', 'b'], ['c', 'd']], dtype=object)>>> print(np.tensordot(c, d))['abbcccdddd' 'aaaaabbbbbbcccccccdddddddd'] 【2x07】numpy.linalg.det()numpy.linalg.det() 函数计算矩阵的行列式。 阵行列式是指矩阵的全部元素构成的行列式,设 A=(aij) 是数域 P 上的一个 n 阶矩阵,则所有 A=(aij) 中的元素组成的行列式称为矩阵 A 的行列式,记为 |A| 或 det(A) 一个 2×2 矩阵的行列式可表示如下: $$ det = \\left[ \\begin{matrix} a & b \\ c & d \\end{matrix} \\right] = ad - bc $$ 应用举例: 1234>>> import numpy as np>>> a = np.array([[1, 2], [3, 4]])>>> print(np.linalg.det(a))-2.0000000000000004 【2x08】numpy.linalg.solve()numpy.linalg.solve() 函数求解线性矩阵方程或线性标量方程组。 $$\\left {\\begin{aligned}3x+y=9 \\x+2y=8\\end{aligned}\\right.\\qquad用矩阵可表示为:\\qquad\\left[\\begin{matrix}3 & 1 \\1 & 2\\end{matrix}\\right]\\left[\\begin{matrix}x & y\\end{matrix}\\right]= \\left[\\begin{matrix}9 & 8\\end{matrix}\\right]$$ 应用举例: 12345>>> import numpy as np>>> a = np.array([[3,1], [1,2]])>>> b = np.array([9,8])>>> print(np.linalg.solve(a, b))[2. 3.] 【2x09】numpy.linalg.inv()numpy.linalg.inv() 函数计算矩阵的逆矩阵。 设 A 是数域上的一个 n 阶矩阵,若在相同数域上存在另一个 n 阶矩阵 B,使得:AB = BA = E,则我们称 B 是 A 的逆矩阵,而 A 则被称为可逆矩阵。注:E 为单位矩阵。 应用举例: 12345678910>>> import numpy as np>>> a = np.array([[1, 2], [3, 4]])>>> b = np.linalg.inv(a)>>> print(b)[[-2. 1. ] [ 1.5 -0.5]]>>> >>> a * b == b * aarray([[ True, True], [ True, True]]) 【3x00】NumPy IO 操作NumPy IO 操作即读写磁盘上的文本数据或二进制数据,在 NumPy 中有专门的 .npy / npy 文件,.npy 文件用于储存单个 Ndarray 对象;.npz 文件用于储存多个 Ndarray 对象。 【3x01】numpy.save()numpy.save() 函数将数组保存到二进制文件(.npy 文件)中。 基本语法:numpy.save(file, arr[, allow_pickle=True, fix_imports=True]) 参数 描述 file 要保存的文件名,可以带路径,文件后缀为 .npy,若路径末尾没有后缀,则会默认加上 .npy 后缀 arr 要保存的数组 allow_pickle bool 值,可选项,是否允许使用 Python pickle 保存数组对象Python pickle 用于在保存到磁盘文件或从磁盘文件读取之前,对对象进行序列化和反序列化pickle 序列化后的数据,可读性差,人一般无法识别 fix_imports bool 值,可选项,强制以 Python 2 兼容方式对 Python 3 上的数组对象进行处理如果 fix_imports 为True,则 pickle 将尝试将新的 Python 3 名称映射到 Python 2 中使用的旧模块名称,以便 pickle 数据流可被 Python 2 读取 应用举例: 123>>> import numpy as np>>> a = np.array([[1,2,3], [4,5,6]])>>> np.save('D:\\\\file\\\\outfile.npy', a) 【3x02】numpy.load()numpy.load() 函数用于读取 .npy / npz 文件里面的内容。 基本语法:numpy.load(file[, mmap_mode=None, allow_pickle=False, fix_imports=True, encoding='ASCII']) 参数 描述 file 要读取的 npy 文件对象 mmap_mode 可选项,读取文件的模式,可选参数 r+ r w+ c,与 Python 读取文件模式类似,模式含义参见 numpy.memmap allow_pickle bool 值,可选项,是否允许使用 Python pickle 保存数组对象Python pickle 用于在保存到磁盘文件或从磁盘文件读取之前,对对象进行序列化和反序列化pickle 序列化后的数据,可读性差,人一般无法识别 fix_imports bool 值,可选项,强制以 Python 2 兼容方式对 Python 3 上的数组对象进行处理如果 fix_imports 为True,则 pickle 将尝试将新的 Python 3 名称映射到 Python 2 中使用的旧模块名称,以便 pickle 数据流可被 Python 2 读取 encoding str 类型,可选项,读取 Python2 字符串时使用什么编码仅当在 Python3 中加载 Python 2 生成的 pickled 文件(包括包含对象数组的 npy/npz 文件)时才有用不允许使用 latin1、ASCII 和 bytes 以外的值,因为它们可能损坏数字数据。默认值为 ASCII 应用举例: 123456>>> import numpy as np>>> a = np.array([[1,2,3], [4,5,6]])>>> np.save('D:\\\\file\\\\outfile.npy', a)>>> np.load('D:\\\\file\\\\outfile.npy')array([[1, 2, 3], [4, 5, 6]]) 【3x03】numpy.savez()numpy.savez() 函数将多个数组保存到二进制文件(.npz 文件)中。 基本语法:numpy.savez(file, *args[, **kwds]) 参数 描述 file 要保存的文件名,可以带路径,文件后缀为 .npz,若路径末尾没有后缀,则会默认加上 .npz 后缀 args 保存的数组,由于 Python 不知道外面 savez 的数组的名称,因此将使用 arr_0,arr_1 等名称保存数组,这些参数可以是任何表达式 kwds 关键字参数,可选项,数组将与关键字名称一起保存在文件中 应用举例: 12345678910111213141516171819>>> import numpy as np>>> a = np.array([[1,2,3],[4,5,6]])>>> b = np.arange(0, 1.0, 0.1)>>> c = np.sin(b) # c 使用关键字参数 sin_array>>> np.savez('D:\\\\file\\\\outfile.npz', a, b, sin_array=c)>>> r = np.load('D:\\\\file\\\\outfile.npz')>>> print(r.files) # 查看各个数组名称['sin_array', 'arr_0', 'arr_1']>>> >>> print(r['arr_0']) # 数组 a[[1 2 3] [4 5 6]]>>> >>> print(r['arr_1']) # 数组 b[0. 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9]>>> >>> print(r['sin_array']) # 数组 c[0. 0.09983342 0.19866933 0.29552021 0.38941834 0.47942554 0.56464247 0.64421769 0.71735609 0.78332691] 【3x04】numpy.savetxt()numpy.savetxt() 函数将数组保存到文本文件中(txt)。 基本语法:numpy.savetxt(fname, X[, fmt='%.18e', delimiter=' ', newline='n', header='', footer='', comments='# ', encoding=None]) 参数 描述 fname 要保存的文件名,可以带路径,如果文件后缀为 .gz,则文件将自动以压缩格式 gzip 保存 X 要保存的数组 fmt 格式序列或多格式字符串,可选项 delimiter 指定各种分隔符、针对特定列的转换器函数、需要跳过的行数等,可选项 newline 字符串或字符分隔线,可选项 header 写入文件开头的字符串,可选项 footer 写入文件末尾的字符串,可选项 comments 注释,在 header 和 footer 字符串之前添加的字符串,可选项 encoding 对输出文件进行编码,可选项 应用举例: 12345678910111213>>> import numpy as np>>> a = np.array([1,2,3,4,5])>>> np.savetxt('D:\\\\file\\\\outfile.txt', a)>>> np.loadtxt('D:\\\\file\\\\outfile.txt')array([1., 2., 3., 4., 5.])>>> >>> b = np.arange(0,10,0.5).reshape(4,-1)>>> np.savetxt('D:\\\\file\\\\outfile2.txt', b, fmt=\"%d\", delimiter=',') # 保存为整数,以逗号分隔>>> np.loadtxt('D:\\\\file\\\\outfile2.txt', delimiter=',') # 读取数据时也要指定相同的分隔符array([[0., 0., 1., 1., 2.], [2., 3., 3., 4., 4.], [5., 5., 6., 6., 7.], [7., 8., 8., 9., 9.]]) 【3x05】numpy.loadtxt()numpy.loadtxt() 函数用于读取文本文件(txt)里面的内容。 基本语法:numpy.loadtxt(fname[, dtype=<class 'float'>, comments='#', delimiter=None, converters=None, skiprows=0, usecols=None, unpack=False, ndmin=0, encoding='bytes', max_rows=None]) 重要参数解释: 参数 描述 fname 要读取的文件,文件名或生成器。如果文件扩展名是 .gz 或 .bz2,则首先将文件解压缩,注意,生成器应返回字节字符串 dtype 可选项,结果数组的数据类型 comments str 或 str 序列,可选项,用于指示注释开始的字符或字符列表 delimiter str 类型,可选项,指定分隔符 skiprows int 类型,可选项,跳过前 n 行,一般用于跳过第一行表头 usecols int 类型的索引值,读取指定的列 unpack bool 值,可选项,如果为True,则会对返回的数组进行转置 ndmin int 类型,可选项,返回的数组将至少具有 ndmin 维度,否则一维轴将被压缩 encoding str 类型,可选项,用于解码输入文件的编码 max_rows int 类型,可选项,读取 skiprows 行之后的最大行内容。默认值是读取所有行 应用举例: 12345>>> import numpy as np>>> a = np.array([1,2,3,4,5])>>> np.savetxt('D:\\\\file\\\\outfile.txt', a)>>> np.loadtxt('D:\\\\file\\\\outfile.txt')array([1., 2., 3., 4., 5.]) 【3x06】numpy.genfromtxt()numpy.genfromtxt() 函数同样用于读取文本文件(txt)里面的内容。该函数比 loadtxt() 函数功能更加强大,genfromtxt() 主要面向结构数组和缺失数据处理。 官方文档介绍:https://numpy.org/doc/1.18/reference/generated/numpy.genfromtxt.html 推荐文章:https://www.cnblogs.com/Simplelee/p/8975763.html 主要语法:numpy.genfromtxt(fname[, dtype=<class 'float'>, comments='#', delimiter=None, skip_header=0, skip_footer=0, converters=None, missing_values=None, filling_values=None, usecols=None, encoding='bytes']) 主要参数解释: 参数 描述 fname 要读取的文件,文件名或生成器。如果文件扩展名是 .gz 或 .bz2,则首先将文件解压缩,注意,生成器应返回字节字符串 dtype 可选项,结果数组的数据类型 comments str 或 str 序列,可选项,用于指示注释开始的字符或字符列表 delimiter str 类型,可选项,指定分隔符 skip_header int 类型,可选项,文件开头要跳过的行数 skip_footer int 类型,可选项,文件末尾要跳过的行数 converters 变量,可选项,将列的数据转换为值的一组函数还可以为丢失的数据提供默认值:converters = {3: lambda s: float(s or 0)} missing_values 变量,可选项,与缺少的数据相对应的字符串集,默认情况下使用空格表示缺失 filling_values 变量,可选项,缺少数据时用作默认值的一组值 usecols 序列,可选项,读取指定的列 encoding str 类型,可选项,用于解码输入文件的编码 应用举例: 12345678910111213>>> import numpy as np>>> a = np.array([1,2,3,4,5])>>> np.savetxt('D:\\\\file\\\\outfile.txt', a)>>> np.genfromtxt('D:\\\\file\\\\outfile.txt')array([1., 2., 3., 4., 5.])>>>>>> b = np.arange(0,10,0.5).reshape(4,-1)>>> np.savetxt('D:\\\\file\\\\outfile2.txt', b, fmt=\"%d\", delimiter=\",\")>>> np.genfromtxt('D:\\\\file\\\\outfile2.txt', delimiter=',')array([[0., 0., 1., 1., 2.], [2., 3., 3., 4., 4.], [5., 5., 6., 6., 7.], [7., 8., 8., 9., 9.]]) 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105511641未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"NumPy","slug":"Python-数据分析/NumPy","permalink":"https://www.itrhx.com/categories/Python-数据分析/NumPy/"}],"tags":[{"name":"NumPy","slug":"NumPy","permalink":"https://www.itrhx.com/tags/NumPy/"},{"name":"矩阵","slug":"矩阵","permalink":"https://www.itrhx.com/tags/矩阵/"},{"name":"线性代数","slug":"线性代数","permalink":"https://www.itrhx.com/tags/线性代数/"},{"name":"IO操作","slug":"IO操作","permalink":"https://www.itrhx.com/tags/IO操作/"}]},{"title":"Python 数据分析三剑客之 NumPy(五):数学 / 算术 / 统计 / 排序 / 条件 / 判断函数合集","slug":"A66-NumPy-05","date":"2020-03-28T12:23:03.859Z","updated":"2020-07-06T13:28:12.800Z","comments":true,"path":"2020/03/28/A66-NumPy-05/","link":"","permalink":"https://www.itrhx.com/2020/03/28/A66-NumPy-05/","excerpt":"","text":"NumPy 系列文章: Python 数据分析三剑客之 NumPy(一):理解 NumPy / 数组基础 Python 数据分析三剑客之 NumPy(二):数组索引 / 切片 / 广播 / 拼接 / 分割 Python 数据分析三剑客之 NumPy(三):数组的迭代与位运算 Python 数据分析三剑客之 NumPy(四):字符串函数总结与对比 Python 数据分析三剑客之 NumPy(五):数学 / 算术 / 统计 / 排序 / 条件 / 判断函数合集 Python 数据分析三剑客之 NumPy(六):矩阵 / 线性代数库与 IO 操作 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105398131未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【1x00】NumPy 函数速查表 NumPy 三角函数 函数 描述 sin() 正弦函数 cos() 余弦函数 tan() 正切函数 arcsin() 反正弦函数 arccos() 反余弦函数 arctan() 反正切函数 NumPy 舍入函数 函数 描述 around() 将指定数字四舍五入到指定的小数位 rint() 将指定数字四舍五入到最近的整数 floor() 返回小于或者等于指定表达式的最大整数,即向下取整 ceil() 返回大于或者等于指定表达式的最小整数,即向上取整 NumPy 算术函数 函数 描述 add() 数组元素加法运算 subtract() 数组元素减法运算 multiply() 数组元素乘法运算 divide() 数组元素除法运算 reciprocal() 返回数组元素的倒数 power() 返回数组元素的乘幂 mod() 返回数组元素的相除后的余数 remainder() 返回数组元素的相除后的余数 NumPy 统计函数 函数 描述 amax() 计算数组元素沿指定轴的最大值 amin() 计算数组元素沿指定轴的最小值 argmax() 计算数组元素沿指定轴的最大值的索引值 argmin() 计算数组元素沿指定轴的最小值的索引值 sum() 计算数组中所有元素的和 cumsum() 返回一个一维数组,每个元素都是之前所有元素的累加和 cumprod() 返回一个一维数组,每个元素都是之前所有元素的累乘积 ptp() 计算数组元素最大值与最小值的差 percentile() 计算多维数组的任意百分位数 median() 计算数组元素的中位数 mean() 计算数组元素的算术平均值 average() 计算数组元素的加权平均值 std() 计算数组元素的标准差 var() 计算数组元素的方差 NumPy 排序函数 sort() 将原数组元素按照从小到大排序 msort() 将原数组元素按照第一个轴的从小到大排序 argsort() 将元素从小到大排列,提取对应的索引值并返回 lexsort() 将多个序列按照从小到大排序,返回其索引值 sort_complex() 对复数数组进行从小到大排序 partition() 对数组进行分区排序 argpartition() 对数组进行分区排序,返回元素的索引值 unique() 将数组元素去重后返回从小到大的排序结果 NumPy 条件函数 nonzero() 返回原数组中非零元素的索引值 where() 返回数组中满足指定条件的元素的索引值 extract() 返回数组中满足指定条件的元素 NumPy 判断函数 any() 至少有一个元素满足指定条件,则返回 True,否则返回 False all() 所有的元素满足指定条件,则返回 True,否则返回 False 【2x00】NumPy 数学函数NumPy 数学函数包含三角函数、舍入函数等。 【2x01】sin() / cos() / tan()numpy.sin()、numpy.cos()、numpy.tan() 分别对应正弦函数、余弦函数、正切函数。 在求三角函数时,会先将角度转化成弧度,在 NumPy 中的转化公式:角度 * numpy.pi/180 应用举例: 12345678>>> import numpy as np>>> a = np.array([0, 30, 45, 60 ,90])>>> print(np.sin(a*np.pi/180))[0. 0.5 0.70710678 0.8660254 1. ]>>> print(np.cos(a*np.pi/180))[1.00000000e+00 8.66025404e-01 7.07106781e-01 5.00000000e-01 6.12323400e-17]>>> print(np.tan(a*np.pi/180))[0.00000000e+00 5.77350269e-01 1.00000000e+00 1.73205081e+00 1.63312394e+16] 【2x02】arcsin() / arccos() / arctan()numpy.arcsin()、numpy.arccos()、numpy.arctan() 分别对应反正弦函数、反余弦函数、反正切函数。 在求三角函数时,会先将角度转化成弧度,在 NumPy 中的转化公式:角度 * numpy.pi/180 arcsin、arccos、arctan 接收的参数是三角函数值,函数返回给定角度的 sin,cos 和 tan 的反三角函数,如果 sinθ=x,那么 arcsinx=θ,其他类似,这些函数的结果可以通过 numpy.degrees() 函数将弧度转换为角度。 12345678910>>> import numpy as np>>> a = np.array([0, 30, 45, 60 ,90])>>> a_sin = np.sin(a*np.pi/180)>>> a_arcsin = np.arcsin(a_sin)>>> print(a_sin) # 角度的正弦值[0. 0.5 0.70710678 0.8660254 1. ]>>> print(a_arcsin) # 角度的反正弦值,返回值的单位为弧度[0. 0.52359878 0.78539816 1.04719755 1.57079633]>>> print(np.degrees(a_arcsin)) # 弧度转化为角度[ 0. 30. 45. 60. 90.] 【2x03】around() / rint() / floor() / ceil()1、numpy.around() 函数将指定数字四舍五入到指定的小数位,可指定保留的小数位数。 基本语法:numpy.around(a[, decimals=0, out=None]) 参数 描述 a 输入数组 decimals int 类型,可选项,舍入的小数位数,默认值为 0,如果为负,整数将四舍五入到小数点左侧的位置 out ndarray 对象,可选项,放置结果的备用输出数组。它必须具有与预期输出相同的形状,但是如有必要,将强制转换输出值的类型 应用举例: 12345678>>> import numpy as np>>> a = np.array([13, 1.4, 6.23, 12.834])>>> print(np.around(a))[13. 1. 6. 13.]>>> print(np.around(a, decimals=1))[13. 1.4 6.2 12.8]>>> print(np.around(a, decimals=-1))[10. 0. 10. 10.] 2、numpy.rint() 函数将指定数字四舍五入到最近的整数,不保留小数位。 应用举例: 123>>> import numpy as np>>> print(np.rint([-1.7, -1.5, -0.2, 0.2, 1.5, 1.7, 2.0]))[-2. -2. -0. 0. 2. 2. 2.] 3、numpy.floor() 函数会返回小于或者等于指定表达式的最大整数,即向下取整。 应用举例: 123>>> import numpy as np>>> print(np.floor([-1.7, -1.5, -0.2, 0.2, 1.5, 1.7, 2.0]))[-2. -2. -1. 0. 1. 1. 2.] 4、numpy.ceil() 函数会返回大于或者等于指定表达式的最小整数,即向上取整。 应用举例: 123>>> import numpy as np>>> print(np.ceil([-1.7, -1.5, -0.2, 0.2, 1.5, 1.7, 2.0]))[-1. -1. -0. 1. 2. 2. 2.] 【3x00】NumPy 算术函数NumPy 算术函数包含了基本的加减乘除运算、求倒数、幂、余数等。 【3x01】add() / subtract() / multiply() / divide()add()、subtract()、multiply()、divide() 分别对应 NumPy 中的加减乘除运算。 注意:两个数组必须具有相同的形状或符合数组的广播规则才能进行运算。 应用举例: 12345678910111213141516171819>>> import numpy as np>>> a = np.array([[0, 1, 2], [3, 4, 5], [6, 7, 8]])>>> b = np.array([5, 5, 5])>>> print(np.add(a, b))[[ 5 6 7] [ 8 9 10] [11 12 13]]>>> print(np.subtract(a, b))[[-5 -4 -3] [-2 -1 0] [ 1 2 3]]>>> print(np.multiply(a, b))[[ 0 5 10] [15 20 25] [30 35 40]]>>> print(np.divide(a, b))[[0. 0.2 0.4] [0.6 0.8 1. ] [1.2 1.4 1.6]] 【3x02】reciprocal() / power() / mod() / remainder()1、numpy.reciprocal() 函数用于返回各数组元素的倒数。 应用举例: 123>>> import numpy as np>>> print(np.reciprocal([0.25, 4, 1, 2.10]))[4. 0.25 1. 0.47619048] 2、numpy.power() 函数将第一个数组中的元素作为底数,计算它与第二个数组中相应元素的幂。 应用举例: 12345678>>> import numpy as np>>> a = np.array([5, 10, 100])>>> b = np.array([3, 2, 1])>>> print(np.power(a, b))[125 100 100]>>>>>> print(np.power(a, 2))[ 25 100 10000] 3、numpy.mod() 与 numpy.remainder() 都可以计算数组中相应元素相除后的余数。 应用举例: 1234567891011>>> import numpy as np>>> a = np.array([10, 15, 20])>>> b = np.array([3, 4, 5])>>> print(np.mod(a, b))[1 3 0]>>> print(np.remainder(a, b))[1 3 0]>>> print(np.mod(a, 6))[4 3 2]>>> print(np.remainder(a, 9))[1 6 2] 【3x03】absolute() / isnan()1、numpy.absolute() 函数用于计算元素的绝对值。 应用举例: 123>>> import numpy as np>>> print(np.absolute([-1.2, 1.2, 13, -10]))[ 1.2 1.2 13. 10. ] 2、numpy.isnan() 函数用于判断元素是否为 NaN(Not a Number)。 应用举例: 1234567>>> import numpy as np>>> np.isnan(np.nan)True>>> np.isnan(np.inf)False>>> print(np.isnan([np.nan, 2, 3, np.nan]))[ True False False True] 【4x00】NumPy 统计函数NumPy 统计函数包含了计算最大值、最小值、最大值与最小值的差、百分位数、中位数、算术平均值、加权平均值、标准差与方差等。 【4x01】amax() / amin()numpy.amax() 和 numpy.amin() 函数分别用于计算数组中的元素沿指定轴的最大值和最小值。 基本语法: numpy.amax(a[, axis=None, out=None, keepdims=<no value>, initial=<no value>, where=<no value>]) numpy.amin(a[, axis=None, out=None, keepdims=<no value>, initial=<no value>, where=<no value>]) 参数解释: 参数 描述 a 待处理的数组对象 axis 指定轴,可选项,整数或者整数元组类型 out ndarray 对象,可选项,放置结果的备用输出数组,必须具有与预期输出数组相同的形状和缓冲区长度 keepdims bool 类型,可选项,是否保持数组的二维特性 initial 初始值标量,可选项,如果设置了标量,则除了元素之间的比较外,还会和标量进行对比 where 比较条件,通常和 initial 参数一起使用如果当前是 amax 函数,where 为 True 时则会比较最大值, Flase 则会比较最小值该参数含义比较模糊,参考资料较少,准确描述请参考官方文档 应用举例: 1234567891011121314151617181920>>> import numpy as np>>> a = np.array([[2, 4], [8, 9]])>>> print(a)[[2 4] [8 9]]>>> >>> print(np.amax(a))9>>> >>> print(np.amax(a, axis=0)) # 元素按行比较[8 9]>>>>>> print(np.amax(a, axis=0, keepdims=True)) # 元素按行比较并保持数组的二维特性[[8 9]]>>> >>> print(np.amax(a, axis=1)) # 元素按列比较[4 9]>>> >>> print(np.amax(a, axis=1, initial=5)) # 元素按列比较(包括标量一起比较)[5 9] 【4x02】argmax() / argmin()numpy.argmax() 和 numpy.argmin() 函数分别沿指定轴返回最大元素和最小元素的索引值。 基本语法:numpy.argmax(a[, axis=None, out=None]);numpy.argmin(a[, axis=None, out=None]) 参数 描述 a 待处理的数组对象 axis 指定轴,可选项,整数或者整数元组类型,若未指定,则在操作前会将数组展开 out 可选项,放置结果的备用输出数组,必须具有与预期输出数组相同的形状和缓冲区长度 应用举例: 123456789101112131415161718>>> import numpy as np>>> a = np.array([[30,40,70],[80,20,10],[50,90,60]])>>> print(a)[[30 40 70] [80 20 10] [50 90 60]]>>> >>> print (np.argmax(a))7>>> >>> print(np.argmin(a))5>>> >>> print(np.argmax(a, axis=0))[1 2 0]>>> >>> print(np.argmin(a, axis=0))[0 1 1] 【4x03】sum()numpy.sum() 函数用于计算所有元素的和。 基本语法:numpy.sum(a, axis=None, dtype=None, out=None, keepdims=<no value>, initial=<no value>, where=<no value>) 参数 描述 a 待处理的数组对象 axis 指定轴,可选项,整数或者整数元组类型,若未指定则运算前会将数组展平 dtype 指定数据类型,可选项 out 可选项,放置结果的备用输出数组,必须具有与预期输出数组相同的形状和缓冲区长度 keepdims bool 类型,可选项,是否保持数组的二维特性 initial 初始值标量,可选项,如果设置了标量,则除了元素之间的求和外,还会和标量进行求和 where 求和条件,总和中要包括的元素 应用举例: 1234567891011>>> import numpy as np>>> print(np.sum([[0, 1], [0, 5]]))6>>> print(np.sum([[0, 1], [0, 5]], axis=0))[0 6]>>> print(np.sum([[0, 1], [0, 5]], axis=1))[1 5]>>> print(np.sum([[0, 1], [np.nan, 5]], where=[False, True], axis=1))[1. 5.]>>> print(np.sum([10], initial=5))15 【4x04】cumsum() / cumprod()numpy.cumsum():返回一个一维数组,每个元素都是之前所有元素的累加和。numpy.cumprod():返回一个一维数组,每个元素都是之前所有元素的累乘积。 基本语法:numpy.cumsum(a, axis=None, dtype=None, out=None);numpy.cumprod(a, axis=None, dtype=None, out=None) 参数 描述 a 待处理的数组对象 axis 指定轴,可选项,整数或者整数元组类型,若未指定则运算前会将数组展平 dtype 指定数据类型,可选项 out 可选项,放置结果的备用输出数组,必须具有与预期输出数组相同的形状和缓冲区长度 应用举例: 1234567891011121314151617181920212223>>> import numpy as np>>> a = np.array([[1,2,3], [4,5,6]])>>> print(np.cumsum(a))[ 1 3 6 10 15 21]>>> >>> print(np.cumsum(a, axis=0))[[1 2 3] [5 7 9]]>>> >>> print(np.cumsum(a, axis=1))[[ 1 3 6] [ 4 9 15]]>>> >>> print(np.cumprod(a))[ 1 2 6 24 120 720]>>> >>> print(np.cumprod(a, axis=0))[[ 1 2 3] [ 4 10 18]]>>> >>> print(np.cumprod(a, axis=1))[[ 1 2 6] [ 4 20 120]] 【4x05】ptp()numpy.ptp() 函数用于计算数组中元素最大值与最小值的差。 基本语法:numpy.ptp(a[, axis=None, out=None, keepdims=<no value>]) 参数 描述 a 待处理的数组对象 axis 指定轴,可选项,整数或者整数元组类型 out 可选项,放置结果的备用输出数组,必须具有与预期输出数组相同的形状和缓冲区长度 keepdims bool 类型,可选项,是否保持数组的二维特性 应用举例: 123456789101112131415161718>>> import numpy as np>>> a = np.array([[2,5,8],[7,6,3],[2,9,4]])>>> print(a)[[2 5 8] [7 6 3] [2 9 4]]>>> print(np.ptp(a))7>>> >>> print(np.ptp(a, axis=0))[5 4 5]>>> >>> print(np.ptp(a, axis=1))[6 4 7]>>> print(np.ptp(a, axis=1, keepdims=True))[[6] [4] [7]] 【4x06】percentile()numpy.percentile() 函数用于计算一个多维数组的任意百分位数。 百分位数:统计学术语,如果将一组数据从小到大排序,并计算相应的累计百分位,则某一百分位所对应数据的值就称为这一百分位的百分位数。可表示为:一组 n 个观测值按数值大小排列。如:处于 p% 位置的值称第 p 百分位数。 基本语法:numpy.percentile(a, q[, axis=None, out=None, overwrite_input=False, interpolation='linear', keepdims=False]) 参数 描述 a 待处理的数组对象 q 要计算的百分位数,在 [0, 100] 之间 axis 指定轴,可选项,整数或者整数元组类型 out 可选项,放置结果的备用输出数组,必须具有与预期输出数组相同的形状和缓冲区长度 overwrite_input bool 值,可选项,如果为True,则允许通过中间计算来修改输入数组 a 以节省内存在这种情况下,此操作完成后 a 的内容是不确定的 interpolation 可选项,指定当所需百分比位于两个数据点 i<j 之间时要使用的插值方法:linear:i + (j - i) * fraction,其中 fraction 是由 i 和 j 包围的索引的分数部分lower:i;higher:j;nearest:i 或 j,以最近者为准;midpoint:(i + j) / 2 keepdims bool 类型,可选项,是否保持数组的二维特性 应用举例: 123456789101112131415161718>>> import numpy as np>>> a = np.array([[10, 7, 4], [3, 2, 1]])>>> print(a)[[10 7 4] [ 3 2 1]]>>> >>> print(np.percentile(a, 50))3.5>>> >>> print(np.percentile(a, 50, axis=0))[6.5 4.5 2.5]>>> >>> print(np.percentile(a, 50, axis=1))[7. 2.]>>> >>> print(np.percentile(a, 50, axis=1, keepdims=True))[[7.] [2.]] 【4x07】median()numpy.median() 函数用于计算数组元素的中位数。 基本语法:numpy.median(a[, axis=None, out=None, overwrite_input=False, keepdims=False]) 参数 描述 a 待处理的数组对象 axis 指定轴,可选项,整数或者整数元组类型 out 可选项,放置结果的备用输出数组,必须具有与预期输出数组相同的形状和缓冲区长度 overwrite_input bool 值,可选项,如果为True,则允许通过中间计算来修改输入数组 a 以节省内存在这种情况下,此操作完成后 a 的内容是不确定的 keepdims bool 类型,可选项,是否保持数组的二维特性 应用举例: 1234567891011121314>>> import numpy as np>>> a = np.array([[10, 7, 4], [3, 2, 1]])>>> print(a)[[10 7 4] [ 3 2 1]]>>>>>> print(np.median(a))3.5>>>>>> print(np.median(a, axis=0))[6.5 4.5 2.5]>>> >>> print(np.median(a, axis=1))[7. 2.] 【4x08】mean()numpy.mean() 函数计算数组中元素的算术平均值。 算术平均值:沿轴的元素的总和除以元素的数量。 基本语法:numpy.mean(a[, axis=None, dtype=None, out=None]) 参数 描述 a 待处理的数组对象 axis 指定轴,可选项,整数或者整数元组类型 dtype 可选项,用于计算平均值的类型 out 可选项,放置结果的备用输出数组,必须具有与预期输出数组相同的形状和缓冲区长度 应用举例: 1234567891011121314>>> import numpy as np>>> a = np.array([[10, 7, 4], [3, 2, 1]])>>> print(a)[[10 7 4] [ 3 2 1]]>>> >>> print(np.mean(a))4.5>>> >>> print(np.mean(a, axis=0))[6.5 4.5 2.5]>>> >>> print(np.mean(a, axis=1))[7. 2.] 【4x09】average()numpy.average() 函数根据在另一个数组中给出的各自的权重来计算数组中元素的加权平均值。 加权平均值:将各数值乘以相应的权数,然后加总求和得到总体值,再除以总的单位数。 例如:现有数组 [1, 2, 3, 4],相应的权重为 [5, 6, 7, 8],则计算方法为:(1*5+2*6+3*7+4*8)/(5+6+7+8)≈ 2.6923 基本语法:numpy.average(a[, axis=None, weights=None, returned=False]) 参数 描述 a 待处理的数组对象 axis 指定轴,可选项,整数或者整数元组类型 weights 可选项,与 a 中的值相关联的权重数组,如果 weights=None,则 a 中的所有数据都具有 1 的权重,相当于 mean 函数 returned 可选项,bool 类型,默认为 False,如果为 True,则返回元组 (加权平均值, 权重),否则只返回加权平均值 应用举例: 12345678910>>> import numpy as np>>> a = np.array([1, 2, 3, 4])>>> print(np.average(a)) # 不指定权重时相当于 mean 函数2.5>>> >>> print(np.average(a, weights=[5, 6, 7, 8]))2.6923076923076925>>> >>> print(np.average(a, weights=[5, 6, 7, 8], returned=True))(2.6923076923076925, 26.0) 【4x10】std() / var()numpy.std() 和 numpy.var() 分别用于计算数组元素的标准差与方差。 标准差是一组数据平均值分散程度的一种度量,标准差是方差的算术平方根。 方差为 S2,标准差为 S,计算公式如下: $$S^2 = \\frac {1} {n} \\sum_{i=1} ^ {n} (x_i - \\overline {x})^2$$ $$S = \\sqrt {S^2}$$ 基本语法: numpy.std(a, axis=None, dtype=None, out=None, ddof=0, keepdims=<no value>) numpy.var(a[, axis=None, dtype=None, out=None, ddof=0, keepdims=<no value>]) 参数 描述 a 待处理的数组对象 axis 指定轴,可选项,整数或者整数元组类型 dtype 可选项,值的数据类型 out 可选项,放置结果的备用输出数组,必须具有与预期输出数组相同的形状和缓冲区长度 ddof 自由度(Delta Degrees of Freedom),计算中使用的除数是 N-ddof,其中 N 表示元素的数量,ddof 默认为零 keepdims bool 类型,可选项,是否保持数组的二维特性 应用举例: 1234567891011121314151617181920212223>>> import numpy as np>>> a = np.array([[1, 2], [3, 4]])>>> print(a)[[1 2] [3 4]]>>> >>> print(np.std(a))1.118033988749895>>> >>> print(np.std(a, axis=0))[1. 1.]>>> >>> print(np.std(a, axis=1))[0.5 0.5]>>> >>> print(np.var(a))1.25>>> >>> print(np.var(a, axis=0))[1. 1.]>>> >>> print(np.var(a, axis=1))[0.25 0.25] 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105398131未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【5x00】NumPy 排序函数NumPy 排序函数中可以指定使用的排序方法,各种分类算法的特征在于它们的平均速度,最坏情况下的性能,工作空间大小以及它们是否稳定,三种算法对比如下: 方法 速度 最坏情况 工作空间 稳定性 快速排序(quicksort) 1 O(n^2) 0 no 归并排序(mergesort) 2 O(n*log(n)) ~n/2 yes 堆排序(heapsort) 3 O(n*log(n)) 0 no 【5x01】sort()numpy.sort() 函数会将原数组元素从小到大排序,返回输入数组的排序副本。 基本语法:numpy.sort(a[, axis=-1, kind='quicksort', order=None]) 参数 描述 a 要排序的数组 axis 要排序的轴,可选项,如果为 None,则在排序之前会将数组展平,默认值为 -1,它沿着最后一个轴排序 kind 排序算法,可选项,默认值为快速排序(quicksort) order 字符串或者字符串列表,可选项,若指定 order 值,将按照该字段进行排序 应用举例: 12345678910111213141516>>> import numpy as np>>> a = np.array([[1,4],[3,1]])>>> print(np.sort(a))[[1 4] [1 3]]>>> >>> print(np.sort(a, axis=None)) # 数组将展平[1 1 3 4]>>> >>> print(np.sort(a, axis=0))[[1 1] [3 4]]>>> >>> print(np.sort(a, axis=1))[[1 4] [1 3]] 123456789101112>>> import numpy as np>>> dtype = [('name', 'S10'), ('height', float), ('age', int)]>>> values = [('Arthur', 1.8, 41), ('Lancelot', 1.9, 38), ('Galahad', 1.7, 38)]>>> a = np.array(values, dtype=dtype)>>> print(a)[(b'Arthur', 1.8, 41) (b'Lancelot', 1.9, 38) (b'Galahad', 1.7, 38)]>>> >>> print(np.sort(a, order='height')) # 按 height 排序[(b'Galahad', 1.7, 38) (b'Arthur', 1.8, 41) (b'Lancelot', 1.9, 38)]>>> >>> print(np.sort(a, order=['age', 'height'])) # 按 age 排序,如果 age 相等,则按 height 排序[(b'Galahad', 1.7, 38) (b'Lancelot', 1.9, 38) (b'Arthur', 1.8, 41)] 【5x02】msort()numpy.msort() 函数将数组按第一个轴从小到大排序,返回排序后的数组副本,相当于 numpy.sort(a, axis=0) 应用举例: 1234567>>> import numpy as np>>> print(np.msort([[1, 4, 5], [2, 1, 3]]))[[1 1 3] [2 4 5]]>>> print(np.sort([[1, 4, 5], [2, 1, 3]], axis=0))[[1 1 3] [2 4 5]] 【5x03】argsort()numpy.argsort() 函数将原数组元素从小到大排列,提取其对应在原数组中的索引值并返回。 举例:原数组为:[3, 1, 2],从小到大排列:[1, 2, 3],排列后的各元素在原数组中对应的索引值:[1, 2, 0] 基本语法:numpy.argsort(a[, axis=-1, kind=None, order=None]) 参数 描述 a 要排序的数组 axis 要排序的轴,可选项,如果为 None,则在排序之前会将数组展平,默认值为 -1,它沿着最后一个轴排序 kind 排序算法,可选项,默认值为快速排序(quicksort) order 字符串或者字符串列表,可选项,若指定 order 值,将按照该字段进行排序 应用举例: 1234567891011121314151617>>> import numpy as np>>> a = np.array([3, 1, 2])>>> print(np.argsort(a))[1 2 0]>>> >>> b = np.array([[0, 3], [2, 2]])>>> print(np.argsort(b))[[0 1] [0 1]]>>> >>> print(np.argsort(b, axis=0))[[0 1] [1 0]]>>> >>> print(np.argsort(b, axis=1))[[0 1] [0 1]] 【5x04】lexsort()numpy.lexsort() 函数使用键序列执行间接稳定排序,用于对多个序列进行排序,返回其索引值。 基本语法:numpy.lexsort(keys, axis=-1) 参数 描述 keys 类似于 (k, N) 的要排序的数组 axis 指定要排序的轴,默认对最后一个轴进行排序 举例:现有数组 a = [1,5,1,4,3,4,4],b = [9,4,0,4,0,2,1],利用 lexsort() 函数排序后的结果为:[2,0,4,6,5,3,1],排序过程如下: 假设数组 a1 为语文成绩:a1 = [1,5,1,4,3,4,4]假设数组 b1 为数学成绩:b1 = [9,4,0,4,0,2,1]数组索引值 c1 为同学姓名: c1 = [0,1,2,3,4,5,6] 1、首先按照语文成绩进行排名:语文成绩(数组 a)从小到大排名:a2 = [1,1,3,4,4,4,5],对应的学生姓名(索引值)为:c2 = [0,2,4,3,5,6,1],现在可以看到:0、2 同学的语文成绩都一样,均为 13、5、6 同学的语文成绩都一样,均为 4 2、接下来,对于语文成绩相同的,按照他们的数学成绩再次进行排名:0、2 同学对应的数学成绩分别为:9 和 0,从小到大再次排序,即 0 排在 2 的后面,对应的学生姓名为 c3 = [2,0,4,3,5,6,1]3、5、6 同学对应的数学成绩分别为:4、2、1,从小到大再次排序后,对应的学生姓名为 c4 = [2,0,4,6,5,3,1],即最终结果。简单来说,先对数组 a 从小到大排序,提取排序后元素对应的索引值,排序后元素有相同的,再根据数组 b 从小到大排序,得到最终的索引值。以上步骤用 numpy.lexsort() 函数实现如下:12345678910111213141516>>> import numpy as np>>> a = [1,5,1,4,3,4,4]>>> b = [9,4,0,4,0,2,1]>>> >>> c1 = np.lexsort((a,b)) # 先按 b 排序,再按照 a 排序>>> c2 = np.lexsort((b,a)) # 先按 a 排序,再按照 b 排序>>> >>> print(c1)[2 4 6 5 3 1 0]>>> print(c2)[2 0 4 6 5 3 1]>>> >>> print([(a[i],b[i]) for i in c1])[(1, 0), (3, 0), (4, 1), (4, 2), (4, 4), (5, 4), (1, 9)]>>> print([(a[i],b[i]) for i in c2])[(1, 0), (1, 9), (3, 0), (4, 1), (4, 2), (4, 4), (5, 4)]123456>>> import numpy as np>>> first_names = ('Hertz', 'Galilei', 'Hertz')>>> second_names = ('Heinrich', 'Galileo', 'Gustav')>>> >>> print(np.lexsort((second_names, first_names))) # 按照字母对应的 ascii 码从小到大进行排序[1 2 0]### 【5x05】sort_complex() numpy.sort_complex() 函数先使用实部,再使用虚部对复数数组进行排序。 复数:把形如 z=a+bi(a,b 均为实数)的数称为复数,其中 a 称为实部,b 称为虚部,i 称为虚数单位。 基本语法:numpy.sort_complex(a) 应用举例: 123456>>> import numpy as np>>> print(np.sort_complex([5, 3, 6, 2, 1]))[1.+0.j 2.+0.j 3.+0.j 5.+0.j 6.+0.j]>>> >>> print(np.sort_complex([1 + 2j, 2 - 1j, 3 - 2j, 3 - 3j, 3 + 5j]))[1.+2.j 2.-1.j 3.-3.j 3.-2.j 3.+5.j] 【5x06】partition()numpy.partition() 函数用于对数组进行分区排序,返回数组的分区副本。 基本语法:numpy.partition(a, kth[, axis=-1, kind='introselect', order=None]) 参数 描述 a 待排序数组 kth 整数或者整数序列,原数组元素中从小到大的第 k 个元素,在排序后的数组中,仍然处于第 k 位置小于该元素的位于该元素的左边,大于该元素的位于该元素的右边,左右两边没有特别的排序要求 axis 指定要排序的轴,可选项,默认对最后一个轴进行排序 kind 排序算法,可选项,默认值为快速排序(quicksort) order 字符串或者字符串列表,可选项,若指定 order 值,将按照该字段进行排序 应用举例: 1234567>>> import numpy as np>>> a = np.array([3, 4, 2, 1])>>> print(np.partition(a, 3)) # 原数组元素中从小到大的第 3 个元素,排序后仍然处于第 3 位置,小于 3 的在前面,大于 3 的在后面,前后无特别排序要求[2 1 3 4]>>> >>> print(np.partition(a, (1, 3))) # 小于 1 的在前面,大于 3 的在后面,1 和 3 之间的在中间[1 2 3 4] 【5x07】argpartition()numpy.argpartition() 函数用于对数组进行分区排序,返回重组后的索引值数组。 利用该函数可以很快地找出第 k 大的数的位置,以及大于 k(排在 k 后面)和小于 k(排在 k 前面)的数的位置。 基本语法:numpy.argpartition(a[, kth, axis=-1, kind='introselect', order=None]) 参数 描述 a 待排序数组 kth 整数或者整数序列,原数组元素中从小到大的第 k 个元素,在排序后的数组中,仍然处于第 k 位置小于该元素的位于该元素的左边,大于该元素的位于该元素的右边,左右两边没有特别的排序要求 axis 指定要排序的轴,可选项,默认对最后一个轴进行排序 kind 排序算法,可选项,默认值为快速排序(quicksort) order 字符串或者字符串列表,可选项,若指定 order 值,将按照该字段进行排序 以下实例将找到数组的第 3 小(索引值 2)的值和第 2 大(索引值 -2)的值: 123456>>> import numpy as np>>> a = np.array([46, 57, 23, 39, 1, 10, 0, 120])>>> print(a[np.argpartition(a, 2)[2]])10>>> print(a[np.argpartition(a, -2)[-2]])57 【5x08】unique()numpy.unique() 函数找到唯一值并返回从小到大的排序结果,类似于 Python 的 set 集合。 应用举例: 12345678>>> import numpy as np>>> print(np.unique([1, 1, 2, 2, 3, 3]))[1 2 3]>>> >>> a = np.array([[1, 0, 0], [1, 0, 0], [2, 3, 4]])>>> print(np.unique(a, axis=0))[[1 0 0] [2 3 4]] 【6x00】NumPy 条件函数【6x01】nonzero()numpy.nonzero() 函数将返回原数组中非零元素的索引值。 基本语法:numpy.nonzero(a) 应用举例: 12345>>> import numpy as np>>> a = np.array([[3, 0, 0], [0, 4, 0], [5, 6, 0]])>>> print(np.nonzero(a))(array([0, 1, 2, 2], dtype=int32), array([0, 1, 0, 1], dtype=int32)) 返回两个数组,其中的元素一一对应,比如数字 3 的索引值为 (0,0),数字 4 的索引值为 (1,1) 【6x02】where()numpy.where() 函数返回数组中满足指定条件的元素的索引值。 基本语法:numpy.where(condition[, x, y]) 参数 描述 condition 判断条件,如果满足该条件,则产生 x,否则产生 y,若未指定 x 与 y,则返回满足该条件元素的索引值 x, y 返回的值为数组对象,如果满足 condition,则产生 x,否则产生 y 应用举例: 12345678>>> import numpy as np>>> a = np.array([12, 7, 9, 10, 21, 5, 11, 1])>>>>>> print(np.where(a>8))(array([0, 2, 3, 4, 6], dtype=int32),) # 返回满足 a>8 的元素的索引值>>> >>> print(np.where(a>8, a, 10*a)) # 如果原数组中的元素 a>8,则返回 a,否则返回 10*a[12 70 9 10 21 50 11 10] 【6x03】extract()numpy.extract() 函数返回数组中满足指定条件的元素。 基本语法:numpy.extract(condition, arr) 参数 描述 condition 判断条件 arr 原数组 应用举例: 1234>>> import numpy as np>>> a = np.array([12, 7, 9, 10, 21, 5, 11, 1])>>> print(np.extract(a>8, a))[12 9 10 21 11] 123456789101112131415>>> import numpy as np>>> a = np.arange(9).reshape(3,3)>>> print(a)[[0 1 2] [3 4 5] [6 7 8]]>>> >>> condition = np.mod(a,2) == 0 # 定义筛选条件(余数为 0,即偶数)>>> print(condition)[[ True False True] [False True False] [ True False True]]>>>>>> print(np.extract(condition, a))[0 2 4 6 8] 【7x00】NumPy 判断函数【7x01】any() / all()numpy.any():如果至少有一个元素满足指定条件,则返回 True,否则返回 False。 numpy.all():如果所有的元素满足指定条件,则返回 True,否则返回 False。 基本语法:numpy.any(a[, axis=None, out=None, keepdims=<no value>]);numpy.all(a[, axis=None, out=None, keepdims=<no value>]) 参数 描述 a 待处理的数组对象 axis 指定轴,可选项,整数或者整数元组类型 out 可选项,放置结果的备用输出数组,必须具有与预期输出数组相同的形状和缓冲区长度 keepdims bool 类型,可选项,是否保持数组的二维特性 应用举例: 1234567>>> import numpy as np>>> print(np.any([[True, False], [True, True]]))True>>> >>> a = np.array([-3, -2, 4, 2, 8, 1])>>> print(np.any(a<0))True 1234567>>> import numpy as np>>> print(np.all([[True, False], [True, True]]))False>>> >>> a = np.array([-3, -2, 4, 2, 8, 1])>>> print(np.all(a<0))False 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105398131未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"NumPy","slug":"Python-数据分析/NumPy","permalink":"https://www.itrhx.com/categories/Python-数据分析/NumPy/"}],"tags":[{"name":"NumPy","slug":"NumPy","permalink":"https://www.itrhx.com/tags/NumPy/"},{"name":"数学函数","slug":"数学函数","permalink":"https://www.itrhx.com/tags/数学函数/"},{"name":"算术函数","slug":"算术函数","permalink":"https://www.itrhx.com/tags/算术函数/"},{"name":"统计函数","slug":"统计函数","permalink":"https://www.itrhx.com/tags/统计函数/"},{"name":"排序函数","slug":"排序函数","permalink":"https://www.itrhx.com/tags/排序函数/"},{"name":"条件函数","slug":"条件函数","permalink":"https://www.itrhx.com/tags/条件函数/"},{"name":"判断函数","slug":"判断函数","permalink":"https://www.itrhx.com/tags/判断函数/"}]},{"title":"Python 数据分析三剑客之 NumPy(四):字符串函数总结与对比","slug":"A65-NumPy-04","date":"2020-03-26T11:11:14.626Z","updated":"2020-07-06T13:28:08.067Z","comments":true,"path":"2020/03/26/A65-NumPy-04/","link":"","permalink":"https://www.itrhx.com/2020/03/26/A65-NumPy-04/","excerpt":"","text":"NumPy 系列文章: Python 数据分析三剑客之 NumPy(一):理解 NumPy / 数组基础 Python 数据分析三剑客之 NumPy(二):数组索引 / 切片 / 广播 / 拼接 / 分割 Python 数据分析三剑客之 NumPy(三):数组的迭代与位运算 Python 数据分析三剑客之 NumPy(四):字符串函数总结与对比 Python 数据分析三剑客之 NumPy(五):数学 / 算术 / 统计 / 排序 / 条件 / 判断函数合集 Python 数据分析三剑客之 NumPy(六):矩阵 / 线性代数库与 IO 操作 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105350414未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【01】NumPy 字符串函数速查表和 Python 一样,NumPy 也可以进行字符串相关操作。字符串函数在字符数组类(numpy.char)中定义。 NumPy 字符串函数速查表 函数 描述 add() 对两个数组的字符串元素进行连接 join() 通过指定分隔符来连接数组中的元素 mod() 格式化字符串,相当于 Python 字符串中的 % 和 format multiply() 按照给定值返回元素多重连接后的字符串 capitalize() 将字符串(字符串可同时包含字母和数字,只要是连续的都会被视为一个同字符串)第一个字母转换为大写 title() 将单词(仅包含字母,若同时包含数字和字母,则数字之后元素被视为另一个单词)第一个字母转换为大写 lower() 将数组中所有的元素转换为小写 upper() 将数组中所有的元素转换为大写 swapcase() 将数组中每个元素字母大写转为小写,小写转为大写 center() 居中字符串,并使用指定字符在左右侧进行填充 ljust() 左对齐字符串,并使用指定字符在右侧进行填充 rjust() 右对齐字符串,并使用指定字符在左侧进行填充 zfill() 在数组元素的左边填充指定个数的数字 0 strip() 移除数组每个元素开头和者结尾处的特定字符 lstrip() 移除数组每个元素开头(最左边)的特定字符 rstrip() 移除数组每个元素结尾(最右边)的特定字符 partition() 指定分割符对字符串进行分割(从最左边的分割符开始分割,仅分割一次,返回三个元素) rpartition() 指定分割符对字符串进行分割(从最右边的分割符开始分割,仅分割一次,返回三个元素) split() 指定分割符对字符串进行分割(从最左边的分割符开始分割,可指定分割次数,返回多个元素) rsplit() 指定分割符对字符串进行分割(从最右边的分割符开始分割,可指定分割次数,返回多个元素) replace() 使用新字符串替换原字符串中的子字符串 splitlines() 以换行符作为分隔符来分割字符串 translate() 将数组元素字符串按照给定的转换表进行映射 encode() 编码操作,数组元素依次调用 str.encode decode() 解码操作,数组元素依次调用 str.decode 【02】numpy.char.add()numpy.char.add() 函数用于对两个数组的字符串元素进行连接。 基本语法:numpy.char.add(x1, x2),数组 x1 和 x2 必须具有相同的形状。 参数 描述 x1 要处理的 str 或 unicode 数组 x2 要处理的 str 或 unicode 数组 应用举例: 12345>>> import numpy as np>>> print(np.char.add(['hello'],[' world']))['hello world']>>> print(np.char.add(['123', 'abc'], [' 456', ' def']))['123 456' 'abc def'] 【03】numpy.char.join()numpy.char.join() 函数通过指定分隔符来连接数组中的元素。 基本语法:numpy.char.join(sep1, seq2) 参数 描述 seq1 分割符,str 或 unicode 数组 seq2 被分割的 str 或 unicode 数组 应用举例: 123456>>> import numpy as np>>> print(np.char.join('-', 'python'))p-y-t-h-o-n>>>>>> print(np.char.join(['+','-'],['python','java']))['p+y+t+h+o+n' 'j-a-v-a'] 【04】numpy.char.mod()numpy.char.mod() 函数用于格式化字符串,相当于 Python 字符串中的 % 和 format。 基本语法:numpy.char.mod(value , a) 1234567>>> import numpy as np>>> print(np.char.mod('value=%.2f', np.arange(6)))['value=0.00' 'value=1.00' 'value=2.00' 'value=3.00' 'value=4.00' 'value=5.00']>>>>>> print(np.char.mod('value=%.4f', [[1.1, 2, 3.021], [4.12, 5, 6.1]]))[['value=1.1000' 'value=2.0000' 'value=3.0210'] ['value=4.1200' 'value=5.0000' 'value=6.1000']] 【05】numpy.char.multiply()numpy.char.multiply() 函数用于元素的多重连接,即返回 a*i。 基本语法:numpy.char.multiply(a, i) 参数 描述 a 要处理的 str 或 unicode 数组 i 整数数组 应用举例: 123>>> import numpy as np>>> print(np.char.multiply('Python ', 4))Python Python Python Python 【06】numpy.char.capitalize()numpy.char.capitalize() 函数将字符串第一个字母转换为大写。 基本语法:numpy.char.capitalize(a) 参数解释:a:要处理的 str 或 unicode 数组。 应用举例: 12345>>> import numpy as np>>> print(np.char.capitalize('python'))Python>>> print(np.char.capitalize(['a1b2','1b2a','b2a1','2a1b']))['A1b2' '1b2a' 'B2a1' '2a1b'] 【07】numpy.char.title()numpy.char.title() 函数将数组元素字符串的每个单词的第一个字母转换为大写。注意:如果一个字符串中间有非字母,则非字母之后的字符串会被视为另一个单词。 基本语法:numpy.char.title(a) 应用举例: 1234567>>> import numpy as np>>> print(np.char.title('i love python!'))I Love Python!>>> print(np.char.title('a1bc2def3h'))A1Bc2Def3H>>> print(np.char.title(['a1bc', 'a 1bc', 'a1 bc', 'a1b c']))['A1Bc' 'A 1Bc' 'A1 Bc' 'A1B C'] 【08】numpy.char.lower()numpy.char.lower() 函数将数组中所有的元素转换为小写。 基本语法:numpy.char.lower(a) 应用举例: 12345>>> import numpy as np>>> print(np.char.lower('PYTHON'))python>>> print(np.char.lower(['PYTHON', 'A123C', 'Ba1A']))['python' 'a123c' 'ba1a'] 【09】numpy.char.upper()numpy.char.upper() 函数将数组中所有的元素转换为大写。 基本语法:numpy.char.upper(a) 应用举例: 12345>>> import numpy as np>>> print(np.char.upper('python'))PYTHON>>> print(np.char.upper(['python', 'a123c', 'ba1A']))['PYTHON' 'A123C' 'BA1A'] 【10】numpy.char.swapcase()numpy.char.swapcase() 函数将数组中每个元素字母大写转为小写,小写转为大写。 基本语法:numpy.char.swapcase(a) 应用举例: 12345>>> import numpy as np>>> print(np.char.swapcase('Abc123DEf456gHI'))aBC123deF456Ghi>>> print(np.char.swapcase(['Abc', '1De', '23F', 'Ghi']))['aBC' '1dE' '23f' 'gHI'] 【11】numpy.char.center()numpy.char.center() 函数用于居中字符串,并使用指定字符在左右侧进行填充。 基本语法:numpy.char.center(a, width[, fillchar=' ']) 参数 描述 a 要处理的 str 或 unicode 数组 width int 类型,结果字符串的总长度 fillchar 可选项,str 或 unicode 数组,要使用的填充字符,默认为空格 应用举例: 1234567>>> import numpy as np>>> print(np.char.center('python', 10)) python >>> print(np.char.center('python', 12, fillchar='-'))---python--->>> print(np.char.center('python', 11, fillchar='-'))---python-- 【12】numpy.char.ljust()numpy.char.ljust() 函数用于左对齐字符串,并使用指定字符在右侧进行填充。 基本语法:numpy.char.ljust(a, width[, fillchar=' ']) 参数 描述 a 要处理的 str 或 unicode 数组 width int 类型,结果字符串的总长度 fillchar 可选项,str 或 unicode 数组,要使用的填充字符,默认为空格 应用举例: 123>>> import numpy as np>>> print(np.char.ljust('python', 10, fillchar='-'))python---- 【13】numpy.char.rjust()numpy.char.ljust() 函数用于右对齐字符串,并使用指定字符在左侧进行填充。 基本语法:numpy.char.rjust(a, width[, fillchar=' ']) 参数 描述 a 要处理的 str 或 unicode 数组 width int 类型,结果字符串的总长度 fillchar 可选项,str 或 unicode 数组,要使用的填充字符,默认为空格 应用举例: 123>>> import numpy as np>>> print(np.char.rjust('python', 10, fillchar='-'))----python 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105350414未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【14】numpy.char.zfill()numpy.char.zfill() 函数在数组元素的左边填充指定个数的数字 0。 基本语法:numpy.char.zfill(a, width) 参数 描述 a 要处理的 str 或 unicode 数组 width int 类型,数组字符串在左边填充 0 后整个字符串的宽度如果宽度小于原字符串的宽度,则结果会去掉原字符串中多余的元素 应用举例: 12345>>> import numpy as np>>> print(np.char.zfill('python', 3))pyt>>> print(np.char.zfill('python', 10))0000python 【15】numpy.char.strip()numpy.char.strip() 函数用于移除开头和结尾处的特定字符。 基本语法:numpy.char.strip(a[, chars=None]) 参数 描述 a 要处理的 str 或 unicode 数组 chars 可选项,str 类型,指定要删除的字符集,如果省略或者为 None,则默认为删除空白 应用举例: 123456>>> import numpy as np>>> print(np.char.strip('alibaba','a'))libab>>> >>> print(np.char.strip(['Alibaba','admin','java', 'ABBA'],'a'))['Alibab' 'dmin' 'jav' 'ABBA'] 【16】numpy.char.lstrip()numpy.char.lstrip() 函数用于移除数组每个元素最右边的特定字符。 基本语法:numpy.char.lstrip(a[, chars=None]) 参数 描述 a 要处理的 str 或 unicode 数组 chars 可选项,str 类型,指定要删除的字符集,如果省略或者为 None,则默认为删除空白 应用举例: 123456>>> import numpy as np>>> print(np.char.lstrip('alibaba','a'))libaba>>> >>> print(np.char.lstrip(['Alibaba','admin','java', 'aBBa'],'a'))['Alibaba' 'dmin' 'java' 'BBa'] 【17】numpy.char.rstrip()numpy.char.rstrip() 函数用于移除数组每个元素最右边的特定字符。 基本语法:numpy.char.rstrip(a[, chars=None]) 参数 描述 a 要处理的 str 或 unicode 数组 chars 可选项,str 类型,指定要删除的字符集,如果省略或者为 None,则默认为删除空白 应用举例: 12345>>> import numpy as np>>> print(np.char.rstrip('alibaba','a'))alibab>>> print(np.char.rstrip(['Alibaba','admin','java', 'aBBa'],'a'))['Alibab' 'admin' 'jav' 'aBB'] 【18】numpy.char.partition()numpy.char.partition() 函数通过指定分割符对字符串进行分割,从最左边第一次出现的分割符开始分割,仅分割一次,返回三个元素。 基本语法:numpy.char.partition(a, sep) 参数 描述 a 要处理的 str 或 unicode 数组 sep 分割字符,str 或 unicode 类型,返回三个元素:分割字符前的字符,分割字符,分割字符后的字符如果元素包含多个分割字符,以最左边的为准,如果找不到分隔符,则返回三个元素:字符串本身以及两个空字符串 应用举例: 123456789>>> print(np.char.partition('111a222','a'))['111' 'a' '222']>>> print(np.char.partition('111a222a333','a'))['111' 'a' '222a333']>>> print(np.char.partition('111a222a333','b'))['111a222a333' '' '']>>> print(np.char.partition(['111a222', '23a45'],'a'))[['111' 'a' '222'] ['23' 'a' '45']] 【19】numpy.char.rpartition()numpy.char.partition() 函数通过指定分割符对字符串进行分割,从最右边第一次出现的分割符开始分割,仅分割一次,返回三个元素。 基本语法:numpy.char.rpartition(a, sep) 参数 描述 a 要处理的 str 或 unicode 数组 sep 分割字符,str 或 unicode 类型,返回三个元素:分割字符前的字符,分割字符,分割字符后的字符如果元素包含多个分割字符,以最右边的为准,如果找不到分隔符,则返回三个元素:两个空字符串以及字符串本身 应用举例: 12345678>>> import numpy as np>>> print(np.char.rpartition('111a222a333','a'))['111a222' 'a' '333']>>> print(np.char.rpartition('111a222a333','b'))['' '' '111a222a333']>>> print(np.char.rpartition(['111a222a333', '23a45'],'a'))[['111a222' 'a' '333'] ['23' 'a' '45']] 【20】numpy.char.split()numpy.char.split() 函数通过指定分割符对字符串进行分割,从最左边的分割符开始分割,可指定分割次数,返回多个元素。 基本语法:numpy.char.split(a[, sep=None, maxsplit=None]) 参数 描述 a 要处理的 str 或 unicode 数组 sep 分隔符,可选项,str 或者 unicode 类型,如果 sep 未指定或者为 None,则默认为空格 maxsplit 可选项,int 类型,如果指定 maxsplit,则最多完成 maxsplit 次分割 应用举例: 123456789>>> import numpy as np>>> print(np.char.split('I love python!'))['I', 'love', 'python!']>>> print(np.char.split('www.itrhx.com', sep='.'))['www', 'itrhx', 'com']>>> print(np.char.split('one.two.itrhx.com', sep='.', maxsplit=2))['one', 'two', 'itrhx.com']>>> print(np.char.split('one.two.itrhx.com', '.', 2))['one', 'two', 'itrhx.com'] 【21】numpy.char.rsplit()numpy.char.split() 函数通过指定分割符对字符串进行分割,从最右边的分割符开始分割,可指定分割次数,返回多个元素。 基本语法:numpy.char.rsplit(a[, sep=None, maxsplit=None]) 参数 描述 a 要处理的 str 或 unicode 数组 sep 分隔符,可选项,str 或者 unicode 类型,如果 sep 未指定或者为 None,则默认为空格 maxsplit 可选项,int 类型,如果指定 maxsplit,则最多完成 maxsplit 次分割 应用举例: 123>>> import numpy as np>>> print(np.char.rsplit('one.two.itrhx.com', '.', 2))['one.two', 'itrhx', 'com'] 【22】numpy.char.replace()numpy.char.replace() 函数可以使用新字符串来替换原字符串中的子字符串。 基本语法:numpy.char.replace(a, old, new[, count=None]) 参数 描述 a 要处理的 str 或 unicode 数组 old 旧的字符串,即要替换的字符串,str 或 unicode 类型 new 新的字符串,即替换的字符串,str 或 unicode 类型 count int 类型,如果指定该值 N,则会替换 old 中出现的前 N 个字符串 应用举例: 123456789>>> import numpy as np>>> print(np.char.replace('i like python', 'python', 'java'))i like java>>> >>> print(np.char.replace('aaaaaaa', 'a', 'b', count=3))bbbaaaa>>>>>> print(np.char.replace('a111a11a1a111aa', 'a', 'A', count=3))A111A11A1a111aa 【23】numpy.char.splitlines()numpy.char.splitlines() 函数以换行符作为分隔符来分割字符串,并返回数组。 基本语法:numpy.char.splitlines(a[, keepends=None]) 参数 描述 a 要处理的 str 或 unicode 数组 keepends 如果指定 keepends 为 True,则换行符会包含在结果列表中,否则不包含 12345>>> import numpy as np>>> print(np.char.splitlines('hi python!\\nhi java!'))['hi python!', 'hi java!']>>> print(np.char.splitlines('hi python!\\nhi java!', keepends=True))['hi python!\\n', 'hi java!'] 【24】numpy.char.translate()numpy.char.translate() 函数将数组元素字符串按照给定的转换表进行映射。 基本语法:numpy.char.translate(a, table[, deletechars=None]) 参数 描述 a 要处理的 str 或 unicode 数组 table 包含 256 个字符的映射表,映射表通过 str.maketrans() 方法转换而来 deletechars 可选项,str 类型,字符串中要过滤的字符列表 应用举例: 123456>>> import numpy as np>>> intab = 'abcdef'>>> outtab = '123456'>>> table = str.maketrans(intab, outtab) # 制作映射表>>> print(np.char.translate('this is a translate example!', table))this is 1 tr1nsl1t5 5x1mpl5! 【25】numpy.char.encode()numpy.char.encode() 函数用于编码操作,数组元素依次调用 str.encode,可以使用 Python 标准库中的编解码器。 基本语法:numpy.char.encode(a[, encoding=None, errors=None]) 参数 描述 a 要处理的 str 或 unicode 数组 encoding 编码名称,可选项,str 类型,默认编码为 utf-8 errors 指定如何处理编码错误,可选项,str 类型 应用举例: 123456>>> import numpy as np>>> print(np.char.encode('python', 'cp500'))b'\\x97\\xa8\\xa3\\x88\\x96\\x95'>>>>>> print(np.char.encode(['aAaAaA', ' aA ', 'abBABba'], 'cp037'))[b'\\x81\\xc1\\x81\\xc1\\x81\\xc1' b'@@\\x81\\xc1@@' b'\\x81\\x82\\xc2\\xc1\\xc2\\x82\\x81'] 【26】numpy.char.decode()numpy.char.decode() 函数用于解码操作,数组元素依次调用 str.decode,可以使用 Python 标准库中的编解码器。 基本语法:numpy.char.decode(a[, encoding=None, errors=None]) 参数 描述 a 要处理的 str 或 unicode 数组 encoding 编码名称,可选项,str 类型,默认编码为 utf-8 errors 指定如何处理编码错误,可选项,str 类型 应用举例: 123456>>> import numpy as np>>> print(np.char.decode(b'\\x97\\xa8\\xa3\\x88\\x96\\x95', 'cp500'))python>>>>>> print(np.char.decode([b'\\x81\\xc1\\x81\\xc1\\x81\\xc1' b'@@\\x81\\xc1@@' b'\\x81\\x82\\xc2\\xc1\\xc2\\x82\\x81'], 'cp500'))['aAaAaA aA abBABba'] 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105350414未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"NumPy","slug":"Python-数据分析/NumPy","permalink":"https://www.itrhx.com/categories/Python-数据分析/NumPy/"}],"tags":[{"name":"NumPy","slug":"NumPy","permalink":"https://www.itrhx.com/tags/NumPy/"},{"name":"字符串函数","slug":"字符串函数","permalink":"https://www.itrhx.com/tags/字符串函数/"}]},{"title":"Python 数据分析三剑客之 NumPy(三):数组的迭代与位运算","slug":"A64-NumPy-03","date":"2020-03-24T10:16:43.374Z","updated":"2020-08-06T03:04:30.457Z","comments":true,"path":"2020/03/24/A64-NumPy-03/","link":"","permalink":"https://www.itrhx.com/2020/03/24/A64-NumPy-03/","excerpt":"","text":"NumPy 系列文章: Python 数据分析三剑客之 NumPy(一):理解 NumPy / 数组基础 Python 数据分析三剑客之 NumPy(二):数组索引 / 切片 / 广播 / 拼接 / 分割 Python 数据分析三剑客之 NumPy(三):数组的迭代与位运算 Python 数据分析三剑客之 NumPy(四):字符串函数总结与对比 Python 数据分析三剑客之 NumPy(五):数学 / 算术 / 统计 / 排序 / 条件 / 判断函数合集 Python 数据分析三剑客之 NumPy(六):矩阵 / 线性代数库与 IO 操作 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105185337未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【1x00】numpy.nditer 迭代器对象numpy.nditer 是 NumPy 的迭代器对象,迭代器对象提供了许多灵活的方法来访问一个或多个数组中的所有元素,简单来说,迭代器最基本的任务就是完成对数组元素的访问。 【1x01】单数组的迭代单数组迭代示例: 12345678910>>> import numpy as np>>> a = np.arange(10).reshape(2, 5)>>> print(a)[[0 1 2 3 4] [5 6 7 8 9]]>>> for i in np.nditer(a): print(i, end=' ') 0 1 2 3 4 5 6 7 8 9 注意:默认对数组元素的访问顺序,既不是以标准 C(行优先) 也不是 Fortran 顺序(列优先),选择的顺序是和数组内存布局一致的,这样做是为了提高访问效率,反映了默认情况下只需要访问每个元素而不关心特定排序的想法。以下用一个数组的转置来理解这种访问机制。 123456789101112131415161718192021222324252627282930313233343536373839>>> import numpy as np>>> a = np.arange(10).reshape(2, 5)>>> print(a)[[0 1 2 3 4] [5 6 7 8 9]]>>> >>> b = a.T>>> print(b)[[0 5] [1 6] [2 7] [3 8] [4 9]]>>> >>> c = a.T.copy(order='C')>>> print(c)[[0 5] [1 6] [2 7] [3 8] [4 9]]>>> >>> for i in np.nditer(a): print(i, end=' ') 0 1 2 3 4 5 6 7 8 9 >>> >>> for i in np.nditer(b): print(i, end=' ') 0 1 2 3 4 5 6 7 8 9 >>> >>> for i in np.nditer(c): print(i, end=' ') 0 5 1 6 2 7 3 8 4 9 例子中 a 是一个 2 行 5 列的数组,b 数组对 a 进行了转置,而 c 数组则是对 a 进行转置后按照 C order(行优先)的形式复制到新内存中储存,b 数组虽然进行了转置操作,但是其元素在内存当中的储存顺序仍然和 a 一样,所以对其迭代的效果也和 a 一样,c 数组元素在新内存当中的储存顺序不同于 a 和 b,因此对其迭代的效果也不一样。 【1x02】控制迭代顺序如果想要按照特定顺序来对数组进行迭代,nditer 同样也提供了 order 参数,可选值为:C F A K numpy.nditer(a, order='C'):标准 C 顺序,即行优先; numpy.nditer(a, order='F'): Fortran 顺序,即列优先; numpy.nditer(a, order='A'):如果所有数组都是 Fortran 顺序的,则 A 表示以 F 顺序,否则以 C 顺序; numpy.nditer(a, order='K'):默认值,保持原数组在内存当中的顺序。 应用举例: 123456789101112131415161718192021222324>>> import numpy as np>>> a = np.arange(12).reshape(3, 4)>>> print(a)[[ 0 1 2 3] [ 4 5 6 7] [ 8 9 10 11]]>>> >>> for i in np.nditer(a, order='C'): print(i, end= ' ') 0 1 2 3 4 5 6 7 8 9 10 11 >>> >>> for i in np.nditer(a, order='F'): print(i, end= ' ') 0 4 8 1 5 9 2 6 10 3 7 11 >>> >>> for i in np.nditer(a, order='K'): print(i, end= ' ') 0 1 2 3 4 5 6 7 8 9 10 11 【1x03】修改数组元素nditer 对象提供了可选参数 op_flags,默认情况下,该参数值为 readonly(只读),如果在遍历数组的同时,要实现对数组中元素值的修改,则可指定 op_flags 值为 readwrite(读写) 或者 writeonly(只读)。 应用举例: 12345678910111213>>> import numpy as np>>> a = np.arange(10).reshape(2, 5)>>> print(a)[[0 1 2 3 4] [5 6 7 8 9]]>>> >>> for i in np.nditer(a, op_flags=['readwrite']): i[...] = i+1 >>> print(a)[[ 1 2 3 4 5] [ 6 7 8 9 10]] 1234567891011121314>>> import numpy as np>>> li = []>>> a = np.arange(12).reshape(3, 4)>>> print(a)[[ 0 1 2 3] [ 4 5 6 7] [ 8 9 10 11]]>>> >>> for i in np.nditer(a, op_flags=['readwrite']): li.append(i*2) >>> print(li)[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22] 【1x04】使用外部循环nditer 对象支持 flags 参数,该参数最常用的值是 external_loop,表示使给定的值为具有多个值的一维数组。 通俗来讲,当 Ndarray 的顺序和遍历的顺序一致时,就会将所有元素组成一个一维数组返回;当 Ndarray 的顺序和遍历的顺序不一致时,则返回每次遍历的一维数组 官方介绍:https://docs.scipy.org/doc/numpy/reference/arrays.nditer.html#using-an-external-loop 应用举例: 123456789101112>>> import numpy as np>>> a = np.array([[0,1,2,3], [4,5,6,7], [8,9,10,11]], order='C')>>> for i in np.nditer(a, flags=['external_loop'], order='C' ): print(i, end=' ') [ 0 1 2 3 4 5 6 7 8 9 10 11] >>> for i in np.nditer(a, flags=['external_loop'], order='F' ): print(i, end=' ') [0 4 8] [1 5 9] [ 2 6 10] [ 3 7 11] 【1x05】跟踪元素索引在迭代期间,我们有可能希望在计算中使用当前元素的索引值,同样可以通过指定 flags 参数的取值来实现: 参数 描述 c_index 跟踪 C 顺序索引 f_index 跟踪 Fortran 顺序索引 multi_index 跟踪多个索引或每个迭代维度一个索引的元组(多重索引) 在以下实例当中: 当参数为 c_index 和 f_index 时,it.index 用于输出元素的索引值 当参数为 multi_index 时,it.multi_index 用于输出元素的索引值 it.iternext() 表示进入下一次迭代,直到迭代完成为止 multi_index 可理解为对迭代对象进行多重索引 1234567891011121314151617181920>>> import numpy as np>>> a = np.arange(6).reshape(2, 3)>>> it = np.nditer(a, flags=['c_index'])>>> while not it.finished: print('%d <%s>' %(it[0], it.index)) it.iternext() 0 <0>True1 <1>True2 <2>True3 <3>True4 <4>True5 <5>False 1234567891011121314151617181920>>> import numpy as np>>> a = np.arange(6).reshape(2, 3)>>> it = np.nditer(a, flags=['f_index'])>>> while not it.finished: print('%d <%s>' %(it[0], it.index)) it.iternext() 0 <0>True1 <2>True2 <4>True3 <1>True4 <3>True5 <5>False 1234567891011121314151617181920>>> import numpy as np>>> a = np.arange(6).reshape(2, 3)>>> it = np.nditer(a, flags=['multi_index'])>>> while not it.finished: print('%d <%s>' %(it[0], it.multi_index)) it.iternext() 0 <(0, 0)>True1 <(0, 1)>True2 <(0, 2)>True3 <(1, 0)>True4 <(1, 1)>True5 <(1, 2)>False 【1x06】广播数组迭代如果两个数组满足广播原则,nditer 对象能够同时迭代它们,即广播数组迭代(多数组的迭代)。 123456789101112131415161718>>> import numpy as np>>> a = np.arange(3)>>> b = np.arange(6).reshape(2,3)>>> print(a)[0 1 2]>>> print(b)[[0 1 2] [3 4 5]]>>> for m, n in np.nditer([a,b]): print(m,n) 0 01 12 20 31 42 5 如果两个数组不满足广播原则,将会抛出异常: 12345678910111213141516>>> import numpy as np>>> a = np.arange(4)>>> b = np.arange(6).reshape(2,3)>>> print(a)[0 1 2 3]>>> print(b)[[0 1 2] [3 4 5]]>>> for m, n in np.nditer([a,b]): print(m,n) Traceback (most recent call last): File \"<pyshell#55>\", line 1, in <module> for m, n in np.nditer([a,b]):ValueError: operands could not be broadcast together with shapes (4,) (2,3) 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105185337未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【2x00】NumPy 位运算由于位运算是直接对整数在内存中的二进制位进行操作,所以有必要先了解一下如何来表示数字的二进制。 在 Python 中,提供了一个内置函数 bin(),将整数转换为以 0b 为前缀的二进制字符串,如果要去掉 0b 前缀,则可以使用 format 方法,因为返回的是字符串,所以也可以使用切片等其他方法去掉前缀。 123456>>> bin(3)'0b11'>>> format(3, 'b')'11'>>> f'{3:b}''11' 除了内置函数以外,NumPy 还提供了一个 numpy.binary_repr 函数,该函数的作用也是以字符串形式返回输入数字的二进制表示形式。 基本语法:numpy.binary_repr(num, width=None) 参数解释: 参数 描述 num 要表示的数,只能是整数形式 width 可选项,对于负数,如果未指定 width,则会在前面添加减号,如果指定了 width,则返回该宽度的负数的二进制补码 123456789>>> import numpy as np>>> np.binary_repr(3)'11'>>> np.binary_repr(-3)'-11'>>> np.binary_repr(-3, width=4)'1101'>>> np.binary_repr(3, width=4)'0011' 以下是 NumPy 数组当中用到的位运算函数,各函数与其对应的用操作符计算的作用相同。 函数 描述 操作符 bitwise_and 对数组元素进行按位与(AND)操作 & bitwise_or 对数组元素进行按位或(OR)操作 \\ bitwise_xor 对数组元素执行按位异或(XOR)操作 ^ invert 对数组元素执行按位取反(NOT)操作 ~ left_shift 将数组元素的二进制形式向左移动指定位,右侧附加相等数量的 0 << right_shift 将数组元素的二进制形式向右移动指定位,左侧附加相等数量的 0 >> 【2x01】numpy.bitwise_and()numpy.bitwise_and() 函数对数组元素进行按位与(AND)操作。 1234567>>> import numpy as np>>> np.binary_repr(10), np.binary_repr(15)('1010', '1111')>>> np.bitwise_and(10, 15)10>>> np.binary_repr(10)'1010' numpy.bitwise_and() 函数还支持多个元素同时进行按位与操作: 123>>> import numpy as np>>> np.bitwise_and([14,3], 13)array([12, 1], dtype=int32) 123456>>> import numpy as np>>> np.bitwise_and([11,7], [4,25])array([0, 1], dtype=int32)>>>>>> np.array([11,7]) & np.array([4,25]) # 函数与 & 操作符作用一样array([0, 1], dtype=int32) 还可以传入布尔值: 123>>> import numpy as np>>> np.bitwise_and([True,False,True],[True,True,True])array([ True, False, True]) 【2x02】numpy.bitwise_or()numpy.bitwise_or() 函数对数组元素进行按位或(OR)操作。 1234567>>> import numpy as np>>> np.binary_repr(10), np.binary_repr(14)('1010', '1110')>>> np.bitwise_or(10, 14)14>>> np.binary_repr(14)'1110' 和按位与操作一样,numpy.bitwise_or() 函数也支持传入布尔值和多个元素同时进行操作: 123456789101112131415>>> import numpy as np>>> np.bitwise_or([33,4], 1)array([33, 5], dtype=int32)>>>>>> np.bitwise_or([33,4], [1,2])array([33, 6], dtype=int32)>>>>>> np.bitwise_or(np.array([2,5,255]), np.array([4,4,4]))array([ 6, 5, 255], dtype=int32)>>>>>> np.array([2,5,255]) | np.array([4,4,4]) # 函数与 | 运算符作用相同array([ 6, 5, 255], dtype=int32)>>>>>> np.bitwise_or([True, True], [False,True])array([ True, True]) 【2x03】numpy.bitwise_xor()numpy.bitwise_xor() 函数对数组元素执行按位异或(XOR)操作。 1234567>>> import numpy as np>>> bin(13), bin(17)('0b1101', '0b10001')>>> np.bitwise_xor(13,17)28>>> bin(28)'0b11100' 123456789101112>>> import numpy as np>>> np.bitwise_xor([31,3], 5)array([26, 6], dtype=int32)>>> >>> np.bitwise_xor([31,3], [5,6])array([26, 5], dtype=int32)>>> >>> np.array([31,3]) ^ np.array([5,6]) # 函数与 ^ 运算符作用相同array([26, 5], dtype=int32)>>>>>> np.bitwise_xor([True, True], [False, True])array([ True, False]) 【2x04】numpy.invert()numpy.invert() 函数将对数组元素执行按位取反(NOT)操作,注意按位取反和取反操作不同。 按位取反通用公式:~x = -(x+1) 我们将原来的数称为 A,按位取反后的数称为 B,按位取反的步骤如下:先求 A 的补码,对 A 的补码每一位取反(包括符号位),得到的数为 B 的补码,将 B 的补码转换为 B 的原码得到最终结果。 分情况具体讨论: 正数按位取反步骤 1、将其转换成二进制形式;2、求其补码(正数的原码、反码、补码都相同);3、将补码每一位进行取反操作(包括符号位); 【经过步骤 3 后的结果为一个二进制形式的负数补码,接下来将这个负数补码转换成原码(负数原码到补码的逆运算)】4、对步骤 3 得到的负数 -1 得到反码;5、对步骤 4 得到的反码再进行取反得到原码;6、将步骤 5 得到的原码转回十进制即是最终结果。负数按位取反步骤1、将其转换成二进制形式;2、求其补码(先求其反码、符号位不变,末尾 +1 得到其补码);3、将补码每一位进行取反操作(包括符号位);【经过步骤 3 后的结果为一个二进制形式的正数,接下来将这个正数转换成原码即可】4、由于正数的原码、反码、补码都相同,所以直接将其转换成十进制即为最终结果。注意:第 3 步的取反操作,包括符号位都要取反,与求反码不同,求反码时符号位不变。 具体计算举例(二进制前 4 位为符号位): 9 的按位取反 ① 原码:0000 1001② 反码:0000 1001③ 补码:0000 1001④ 取反:1111 0110 (包括符号位一起取反,得到新的补码)⑤ 反码:1111 0101 (将新的补码 -1 得到其反码)⑥ 原码:1111 1010 (将反码取反得到原码)⑦ 转为十进制:-10 -9 的按位取反 ① 原码:1111 1001② 反码:1111 0110③ 补码:1111 0111④ 取反:0000 1000 (包括符号位一起取反,得到新的补码)⑤ 原码:0000 1000 (由于新的补码为正数,所以原码补码相同)⑥ 转为十进制:8 其他关于按位取反操作的知识: 按位取反运计算方法 [干货]按位取反怎么算?~图文详解 Python 代码应用示例: 1234567>>> import numpy as np>>> np.binary_repr(9, width=8)'00001001'>>> np.invert(9)-10>>> np.invert(-9)8 【2x05】numpy.left_shift()numpy.left_shift() 函数将数组元素的二进制形式向左移动指定位,右侧附加相等数量的 0。 应用举例: 123456789>>> import numpy as np>>> np.binary_repr(10, width=8)'00001010'>>> np.left_shift(10, 2)40>>> 10 << 2 # numpy.left_shift 函数相当于 Python 当中的 << 运算符40>>> np.binary_repr(40, width=8)'00101000' 【2x06】numpy.right_shift()numpy.right_shift() 函数将数组元素的二进制形式向右移动指定位,左侧附加相等数量的 0 123456789>>> import numpy as np>>> np.binary_repr(10, width=8)'00001010'>>> np.right_shift(10, 2)2>>> 10 >> 2 # numpy.right_shift 函数相当于 Python 当中的 >> 运算符2>>> np.binary_repr(2, width=8)'00000010' 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105185337未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"NumPy","slug":"Python-数据分析/NumPy","permalink":"https://www.itrhx.com/categories/Python-数据分析/NumPy/"}],"tags":[{"name":"NumPy","slug":"NumPy","permalink":"https://www.itrhx.com/tags/NumPy/"},{"name":"迭代","slug":"迭代","permalink":"https://www.itrhx.com/tags/迭代/"},{"name":"位运算","slug":"位运算","permalink":"https://www.itrhx.com/tags/位运算/"}]},{"title":"Python 数据分析三剑客之 NumPy(二):数组索引 / 切片 / 广播 / 拼接 / 分割","slug":"A63-NumPy-02","date":"2020-03-22T07:16:24.367Z","updated":"2020-08-06T03:03:09.474Z","comments":true,"path":"2020/03/22/A63-NumPy-02/","link":"","permalink":"https://www.itrhx.com/2020/03/22/A63-NumPy-02/","excerpt":"","text":"NumPy 系列文章: Python 数据分析三剑客之 NumPy(一):理解 NumPy / 数组基础 Python 数据分析三剑客之 NumPy(二):数组索引 / 切片 / 广播 / 拼接 / 分割 Python 数据分析三剑客之 NumPy(三):数组的迭代与位运算 Python 数据分析三剑客之 NumPy(四):字符串函数总结与对比 Python 数据分析三剑客之 NumPy(五):数学 / 算术 / 统计 / 排序 / 条件 / 判断函数合集 Python 数据分析三剑客之 NumPy(六):矩阵 / 线性代数库与 IO 操作 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/104988137未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【1x00】认识 Numpy 中的 nan 和 infnan(NAN,Nan):全称 not a number,即不是一个数字inf(-inf,inf):全称 infinity,inf 表示正无穷,-inf 表示负无穷 以下情况 Numpy 中会出现 nan: 当读取本地的文件为 float 的时候,如果有缺失,就会出现 nan 当做了一个不合适的计算的时候(如:无穷大减去无穷大) 以下情况会出现 inf 或者 -inf: 比如一个数字除以 0,在 Python 中直接会报错,但在 Numpy 中则是一个 inf 或者 -inf 指定一个 nan 或者 inf: 1234567891011>>> import numpy as np>>> a = np.nan>>> b = np.inf>>> print(a)nan>>> print(b)inf>>> type(a)<class 'float'>>>> type(b)<class 'float'> 注意两个 nan 并不相等,而两个 inf 是相等的 1234>>> np.nan == np.nanFalse>>> np.inf == np.infTrue nan 和任何值计算都为 nan: 12345>>> import numpy as np>>> a = np.array([1, 2, 3, 4, 5])>>> b = np.array([1, 2, np.nan, 4, np.nan])>>> print(a+b)[ 2. 4. nan 8. nan] 【1x01】判断是否为 nan 和 inf isnan:判断元素是否为 nan(非数字) isinf:判断元素是否为正无穷大或负无穷大 isposinf:判断元素是否为正无穷大 isneginf:判断元素是否为负无穷大 isfinite:判断元素是否为有限的(不是非数字,正无穷大和负无穷大中的一个) 12345678910>>> import numpy as np>>> a = np.array([[1, 2, 3, 4], [np.nan, 6, 7, np.nan], [9 ,np.nan, 10, 11]])>>> print(a)[[ 1. 2. 3. 4.] [nan 6. 7. nan] [ 9. nan 10. 11.]]>>> np.isnan(a)array([[False, False, False, False], [ True, False, False, True], [False, True, False, False]]) 【1x02】统计数组中 nan 的个数1、利用 np.count_nonzero 方法,结合两个 nan 不相等的属性,可以判断数组中 nan 的个数: 12345678>>> import numpy as np>>> a = np.array([[1, 2, 3, 4], [np.nan, 6, 7, np.nan], [9 ,np.nan, 10, 11]])>>> print(a)[[ 1. 2. 3. 4.] [nan 6. 7. nan] [ 9. nan 10. 11.]]>>> np.count_nonzero(a != a) # 判断 nan 的个数,a != a 即为 nan,nan != nan3 2、isnan() 方法可以判断哪些是 nan,再结合 np.count_nonzero 方法可以判断数组中 nan 的个数: 123456789101112>>> import numpy as np>>> a = np.array([[1, 2, 3, 4], [np.nan, 6, 7, np.nan], [9 ,np.nan, 10, 11]])>>> print(a)[[ 1. 2. 3. 4.] [nan 6. 7. nan] [ 9. nan 10. 11.]]>>> np.isnan(a) # np.isnan(a) 与 a != a 效果相同,判断那些是 nanarray([[False, False, False, False], [ True, False, False, True], [False, True, False, False]])>>> np.count_nonzero(np.isnan(a))3 3、利用 collections 模块的 Counter 方法来统计 nan 的个数(此方法仅适用于一维数组): 12345>>> from collections import Counter>>> import numpy as np>>> a = np.array([1, 2, np.nan, 4, np.nan])>>> print(Counter(np.isnan(a)))Counter({False: 3, True: 2}) 【1x03】统计数组中 inf 的个数1、isinf() 方法可以判断哪些是 inf,再结合 np.count_nonzero 方法可以判断数组中 inf 的个数: 123456789101112>>> import numpy as np>>> a = np.array([[1, 2, 3, 4], [np.inf, 6, 7, -np.inf], [9 ,-np.inf, 10, 11]])>>> print(a)[[ 1. 2. 3. 4.] [ inf 6. 7. -inf] [ 9. -inf 10. 11.]]>>> np.isinf(a)array([[False, False, False, False], [ True, False, False, True], [False, True, False, False]])>>> np.count_nonzero(np.isinf(a))3 2、利用 collections 模块的 Counter 方法来统计 inf 的个数(此方法仅适用于一维数组): 12345>>> from collections import Counter>>> import numpy as np>>> a = np.array([1, 2, np.inf, 4, -np.inf])>>> print(Counter(np.isinf(a)))Counter({False: 3, True: 2}) 【1x04】替换 inf 和 nannumpy.nan_to_num() 方法可以将 nan 替换为零,将 inf 替换为有限数。 12345678910>>> import numpy as np>>> a = np.array([[1, np.nan, 3, 4], [np.inf, 6, 7, -np.inf], [9 ,-np.inf, 10, np.nan]])>>> print(a)[[ 1. nan 3. 4.] [ inf 6. 7. -inf] [ 9. -inf 10. nan]]>>> print(np.nan_to_num(a))[[ 1.00000000e+000 0.00000000e+000 3.00000000e+000 4.00000000e+000] [ 1.79769313e+308 6.00000000e+000 7.00000000e+000 -1.79769313e+308] [ 9.00000000e+000 -1.79769313e+308 1.00000000e+001 0.00000000e+000]] 如果要将 nan 和 inf 替换成特定的值,则可以用以下方法: 1234567891011121314>>> import numpy as np>>> a = np.array([[1, np.nan, 3, 4], [np.inf, 6, 7, -np.inf], [9 ,-np.inf, 10, np.nan]])>>> print(a)[[ 1. nan 3. 4.] [ inf 6. 7. -inf] [ 9. -inf 10. nan]]>>> loc_nan = np.isnan(a)>>> loc_inf = np.isinf(a)>>> a[loc_nan] = 111>>> a[loc_inf] = 222>>> print(a)[[ 1. 111. 3. 4.] [222. 6. 7. 222.] [ 9. 222. 10. 111.]] 【2x00】NumPy 索引【2x01】获取具体元素NumPy 的索引和 Python 列表的索引类似,可以通过中括号指定索引获取第 i 个值(从 0 开始计数): 12345678910>>> import numpy as np>>> a = np.array([1, 2, 3, 4])>>> print(a[0])1>>> print(a[2])3>>> print(a[-1])4>>> print(a[-2])3 在多维数组中,可以用逗号分隔的索引元组获取具体某个元素: 12345678910>>> import numpy as np>>> a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])>>> print(a)[[1 2 3] [4 5 6] [7 8 9]]>>> print(a[1, 2])6>>> print(a[0, -2])2 1234567891011121314>>> a = np.array([[[1,2,3,4], [5,6,7,8]], [[9,10,11,12], [13,14,15,16]], [[17,18,19,20], [21,22,23,24]]])>>> print(a)[[[ 1 2 3 4] [ 5 6 7 8]] [[ 9 10 11 12] [13 14 15 16]] [[17 18 19 20] [21 22 23 24]]]>>> print(a[0, 1, 2])7>>> print(a[1, 0, -1])12 【2x02】获取行或列1234567891011121314151617181920212223242526272829303132>>> a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]])>>> print(a)[[ 1 2 3] [ 4 5 6] [ 7 8 9] [10 11 12]]>>> print(a[1]) # 取一整行[4 5 6]>>> print(a[1, :]) # 取一整行,效果同上[4 5 6]>>> print(a[1, ...]) # 取一整行,效果同上[4 5 6]>>> print(a[:, 2]) # 取一整列[ 3 6 9 12]>>> print(a[..., 2]) # 取一整列,效果同上[ 3 6 9 12]>>> print(a[1:3]) # 取多行[[4 5 6] [7 8 9]]>>> print(a[:, 0:2]) # 取多列[[ 1 2] [ 4 5] [ 7 8] [10 11]]>>> print(a[[1, 3], :]) # 取第一、三行和所有列[[ 4 5 6] [10 11 12]]>>> print(a[:, [0, 2]]) # 取第零、二列和所有行[[ 1 3] [ 4 6] [ 7 9] [10 12]] 【2x03】布尔索引除了直接获取元素以外,还可以通过一个布尔数组来索引目标数组,即通过布尔运算(如:比较运算符)来获取符合指定条件的元素的数组。 以下实例将筛选出大于 6 的元素: 12345678>>> a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]])>>> print(a)[[ 1 2 3] [ 4 5 6] [ 7 8 9] [10 11 12]]>>> print(a[a > 6])[ 7 8 9 10 11 12] 以下实例使用取补运算符 ~ 来过滤掉 inf: 123456>>> import numpy as np>>> a = np.array([1, 2, np.inf, 3, -np.inf, 4, 5])>>> print(a)[ 1. 2. inf 3. -inf 4. 5.]>>> print(a[~np.isinf(a)])[1. 2. 3. 4. 5.] 【2x04】花式索引花式索引:传递一个索引数组来一次性获得多个数组元素,花式索引总是将数据复制到新数组中。 花式索引根据索引数组的值作为目标数组的某个轴的下标来取值。对于使用一维整型数组作为索引,如果目标是一维数组,那么索引的结果就是对应位置的元素;如果目标是二维数组,那么就是对应下标的行。 花式索引结果的形状与索引数组的形状一致,而不是与被索引数组的形状一致。 一维数组中的应用: 1234>>> import numpy as np>>> a = np.array([1, 2, 3, 4, 5, 6])>>> print([a[0], a[2], a[-1]])[1, 3, 6] 12345>>> import numpy as np>>> a = np.array([1, 2, 3, 4, 5, 6])>>> ind = [0, 2, -1]>>> print(a[ind])[1 3 6] 二维数组中的应用: 12345678>>> import numpy as np>>> a = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])>>> print(a)[[ 1 2 3 4] [ 5 6 7 8] [ 9 10 11 12]]>>> print([a[0, 1], a[1, 2], a[2, 3]])[2, 7, 12] 123456>>> import numpy as np>>> a = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])>>> row = np.array([0, 1, 2]) # 行>>> col = np.array([1, 2, 3]) # 列>>> print(a[row, col])[ 2 7 12] 【3x00】NumPy 切片Ndarray 数组对象基于 0 - n 的下标进行索引,与 Python 中列表的切片操作一样,NumPy 的切片也可以通过冒号分隔切片参数 [start:stop:step] 来进行切片操作,另外,NumPy 也提供了一个内置函数 slice(start, stop, step) 来进行切片操作。 slice 方法应用: 1234567>>> import numpy as np>>> a = np.arange(10)>>> print(a)[0 1 2 3 4 5 6 7 8 9]>>> b = slice(2, 8, 2) # 从索引 2 开始到索引 8 停止,步长为 2>>> print(a[b])[2 4 6] 通过冒号分隔切片参数 [start:stop:step] 来进行切片: 12345678>>> import numpy as np>>> a = np.arange(12)>>> print(a)[ 0 1 2 3 4 5 6 7 8 9 10 11]>>> print(a[1:9:2]) # 从索引 1 开始到索引 9 停止,步长为 2[1 3 5 7]>>> print(a[5:])[ 5 6 7 8 9 10 11] # 从索引 5 开始一直到最后一个元素 二数组中的切片,格式类似于 a[start:stop:step, start:stop:step],以逗号来分割行与列。 12345678910111213141516171819202122232425>>> import numpy as np>>> a = np.array([[1, 2, 3], [4, 5, 6], [7, 8 ,9], [10, 11, 12], [13, 14, 15], [16, 17, 18]])>>> print(a)[[ 1 2 3] [ 4 5 6] [ 7 8 9] [10 11 12] [13 14 15] [16 17 18]]>>> print(a[0:4:2]) # 按照行从索引 0 开始到索引 4 停止,步长为 2[[1 2 3] [7 8 9]]>>> print(a[0:4:2,...]) # 与上述方法相同,... 与 : 作用相同[[1 2 3] [7 8 9]]>>> print(a[...,1:]) # 按照列从索引 1 开始一直到最后一个元素[[ 2 3] [ 5 6] [ 8 9] [11 12] [14 15] [17 18]]>>> print(a[1:5:2,1:]) # 分别按照行从索引 1 开始到索引 5 停止,步长为 2,按照列从索引 1 开始一直到最后一个元素[[ 5 6] [11 12]] 三数组中的切片,格式类似于 a[start:stop:step, start:stop:step, start:stop:step],以逗号来分割块、行与列。 1234567891011121314151617181920>>> import numpy as np>>> a = np.array([[[1,2,3,4], [5,6,7,8]], [[9,10,11,12], [13,14,15,16]], [[17,18,19,20], [21,22,23,24]]])>>> print(a)[[[ 1 2 3 4] [ 5 6 7 8]] [[ 9 10 11 12] [13 14 15 16]] [[17 18 19 20] [21 22 23 24]]]>>> print(a[1,:,0:2]) # 取索引为 1 的块数、所有行、索引为 0 到 2 的列[[ 9 10] [13 14]]>>> print(a[1,:,:]) # 取索引为 1 的块数、所有行与列[[ 9 10 11 12] [13 14 15 16]]>>> print(a[1,...,:]) # ... 与 : 作用相同[[ 9 10 11 12] [13 14 15 16]] 【4x00】NumPy 数组运算以及广播原则NumPy 数组与数之间、数组与数组之间都支持加减乘除的计算。 对于数组与数之间的计算,由于 NumPy 的广播机制,加减乘除都会对数组的每一个元素进行操作。 对于数组与数组之间的计算,相同维度的,相同位置元素之间会进行计算,不同维度的,将自动触发广播机制。 广播(Broadcast)原则:如果两个数组的后缘维度(trailing dimension,即从末尾开始算起的维度)的轴长度相符,或其中的一方的长度为 1,则认为它们是广播兼容的。广播会在缺失和(或)长度为 1 的维度上进行。通俗理解,以下情况的两个数组均可进行广播:1、两个数组各维度大小从后往前比对均一致: 123456789101112131415161718192021>>> import numpy as np>>> a = np.ones((3, 4, 5))>>> b = np.ones((4, 5))>>> print((a+b).shape)(3, 4, 5)>>>>>> >>> c = np.ones((3))>>> d = np.ones((2, 3))>>> print((c+d).shape)(2, 3)>>>>>> >>> e = np.ones((3, 4, 5))>>> f = np.ones((4, 2))>>> print((e+f).shape)Traceback (most recent call last): File \"<pyshell#16>\", line 1, in <module> print((e+f).shape)ValueError: operands could not be broadcast together with shapes (3,4,5) (4,2) # 因为 e 和 f 维度大小此前往后对比不一致,所以会抛出异常 2、两个数组存在一些维度大小不相等时,在其中一个数组中,这个不相等的维度大小为 1: 123456>>> import numpy as np>>> a = np.ones((3, 4, 5))>>> b = np.ones((4, 1))>>> print((a+b).shape)(3, 4, 5)# 此时虽然 a 与 b 的维度大小此前往后对比不一致,但是 b 数组的这个维度大小为 1,所以仍然可以相加 那么当两个数组之间可以进行广播的时候,具体是怎样广播、怎样计算的呢?以下通过代码和图解来更进一步理解广播机制: 数组与数之间的运算: 123456789101112>>> import numpy as np>>> a = np.array([1, 2, 3, 4, 5])>>> print(a)[1 2 3 4 5]>>> print(a+1) # 对数组每一个元素都 +1[2 3 4 5 6]>>> print(a-1) # 对数组每一个元素都 -1[0 1 2 3 4]>>> print(a*2) # 对数组每一个元素都 *1[ 2 4 6 8 10]>>> print(a/2) # 对数组每一个元素都 /1[0.5 1. 1.5 2. 2.5] 相同维度的数组与数组之间的运算: 123456789101112131415161718192021>>> import numpy as np>>> a = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]])>>> b = np.array([[11, 12, 13, 14, 15], [16, 17, 18, 19, 20]])>>> print(a)[[ 1 2 3 4 5] [ 6 7 8 9 10]]>>> print(b)[[11 12 13 14 15] [16 17 18 19 20]]>>> print(a+b)[[12 14 16 18 20] [22 24 26 28 30]]>>> print(b-a)[[10 10 10 10 10] [10 10 10 10 10]]>>> print(a*b)[[ 11 24 39 56 75] [ 96 119 144 171 200]]>>> print(b/a)[[11. 6. 4.33333333 3.5 3. ] [ 2.66666667 2.42857143 2.25 2.11111111 2. ]] 不同维度的数组与数组之间的运算: 实例一:一个二维数组与一个一维数组相加,此时就会触发广播机制,代码与图解如下: 123456789101112131415>>> import numpy as np>>> a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]])>>> b = np.array([20, 20, 20])>>> print(a)[[ 1 2 3] [ 4 5 6] [ 7 8 9] [10 11 12]]>>> print(b)[20 20 20]>>> print(a+b)[[21 22 23] [24 25 26] [27 28 29] [30 31 32]] 实例二:一个 4 行 3 列的二维数组与一个 4 行 1 列的二维数组相加,此时就会触发广播机制,代码与图解如下: 123456789101112131415161718>>> import numpy as np>>> a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]])>>> b = np.array([[1], [2], [3], [4]])>>> print(a)[[ 1 2 3] [ 4 5 6] [ 7 8 9] [10 11 12]]>>> print(b)[[1] [2] [3] [4]]>>> print(a+b)[[ 2 3 4] [ 6 7 8] [10 11 12] [14 15 16]] 实例三:一个 3 块 4 行 2 列,即 shape=(3, 4, 2) 的三维数组与一个 1 块 4 行 2 列,即 shape=(1, 4, 2) 的三维数组相加,此时就会触发广播机制,代码与图解如下: 123456789101112131415161718192021222324252627282930313233343536373839404142>>> import numpy as np>>> a = np.array([[[1, 2], [3, 4], [5, 6], [7, 8]], [[9, 10], [11, 12], [13, 14], [15, 16]], [[17, 18], [19, 20], [21, 22], [23, 24]]])>>> b = np.array([[[1, 2], [3, 4], [5, 6], [7, 8]]])>>> print(a)[[[ 1 2] [ 3 4] [ 5 6] [ 7 8]] [[ 9 10] [11 12] [13 14] [15 16]] [[17 18] [19 20] [21 22] [23 24]]]>>> print(b)[[[1 2] [3 4] [5 6] [7 8]]]>>> print(a.shape)(3, 4, 2)>>> print(b.shape)(1, 4, 2)>>> print(a+b)[[[ 2 4] [ 6 8] [10 12] [14 16]] [[10 12] [14 16] [18 20] [22 24]] [[18 20] [22 24] [26 28] [30 32]]] 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/104988137未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【5x00】数组的拼接与元素的添加【5x01】将数组转换成列表,拼接完成再转换成数组数组的拼接,可以先将数组转成列表,利用列表的拼接函数,如:append()、extend() 等进行拼接处理,然后再将列表转成数组即可。 1234567891011121314>>> import numpy as np>>> a = np.array([1, 2, 3])>>> b = np.array([4, 5, 6])>>> a_list = list(a)>>> b_list = list(b)>>> for i in b_list: a_list.append(i) >>> a_list[1, 2, 3, 4, 5, 6]>>> a = np.array(a_list)>>> aarray([1, 2, 3, 4, 5, 6]) 【5x02】numpy.append()numpy.append() 方法可以将一个数组附加到另一个数组的尾部,与 Python 列表中的 append 方法类似,仅支持两个数组之间的拼接,不能一次性拼接多个数组。 基本语法:numpy.append(a, values, axis=None) 参数解释: 参数 描述 a 被添加元素的目标数组 values 要添加元素的目标数组,即将该数组添加到 a 数组的尾部如果指定了 axis 的值,则要求该数组的维度必须与 a 相同 axis 指定轴,按照指定轴的方向进行拼接如果未指定轴,则在使用前会将 a 和 values 都展平 应用举例: 1234567891011121314151617>>> import numpy as np>>> a = np.array([1, 2, 3])>>> b = np.array([4, 5, 6])>>> print(np.append(a, b)) # 两个一维数组进行拼接[1 2 3 4 5 6]>>> >>> a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])>>> b = np.array([10, 11, 12])>>> print(np.append(a, b)) # 一维数组与二维数组进行拼接[ 1 2 3 4 5 6 7 8 9 10 11 12]>>> >>> a = np.array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]])>>> b = np.array([[13, 14, 15], [16, 17, 18]])>>> print(np.append(a, b)) # 二维数组与三维数组进行拼接[ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18]>>> print(type(np.append(a, b))) # 拼接后的数组仍然是一个 ndarray 对象<class 'numpy.ndarray'> 指定 axis 的值举例(指定了 axis 的值,要求两个数组的维度必须相同): 123456789101112131415>>> import numpy as np>>> a = np.array([[1, 2, 3], [4, 5, 6]])>>> b = np.array([[7, 8, 9]])>>> print(np.append(a, b, axis=0)) # a、b 均为二维数组,指定 axis 值为 0[[1 2 3] [4 5 6] [7 8 9]]>>>>>> a = np.array([[1, 2, 3], [4, 5, 6]])>>> b = np.array([7, 8, 9])>>> print(np.append(a, b, axis=0)) # a 为二维数组,b 为一维数组,指定 axis 值为 0,此时会报错Traceback (most recent call last): ... ...ValueError: all the input arrays must have same number of dimensions, ... 【5x03】numpy.concatenate()numpy.concatenate() 方法能够一次完成多个相同形状数组的拼接。该方法效率更高,适合大规模的数据拼接。 基本语法:numpy.concatenate((a1, a2, ...), axis=0) 参数解释: 参数 描述 a1, a2, … 要拼接的多个数组,要求各数组形状相同 axis 指定轴,按照指定轴的方向进行拼接,默认为 0 轴 要求各数组形状相同举例:如果 axis=0,则要求 1 轴(竖轴)相同,如果 axis=1,则要求 0 轴(横轴)相同。应用举例:1234567891011>>> import numpy as np>>> a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])>>> b = np.array([[10, 11, 12], [13, 14, 15]])>>> c = np.array([[16, 17, 18]]) # 三个二维数组的 1 轴(竖轴)相同>>> print(np.concatenate((a, b, c))) # 三个二维数组默认沿 0 轴(横轴)进行拼接[[ 1 2 3] [ 4 5 6] [ 7 8 9] [10 11 12] [13 14 15] [16 17 18]]1234567>>> import numpy as np>>> a = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])>>> b = np.array([[9, 10, 11], [12, 13, 14]])>>> c = np.array([[15, 16], [17, 18]]) # 三个二维数组的 0 轴(横轴)相同>>> print(np.concatenate((a, b, c), axis=1)) # 三个二维数组沿 1 轴(竖轴)进行拼接[[ 1 2 3 4 9 10 11 15 16] [ 5 6 7 8 12 13 14 17 18]]123456789>>> import numpy as np>>> a = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])>>> b = np.array([[9, 10, 11], [12, 13, 14]])>>> c = np.array([[15, 16], [17, 18]]) # 三个二维数组的 0 轴(横轴)相同>>> print(np.concatenate((a, b, c), axis=0)) # 三个二维数组沿 0 轴(横轴)进行拼接将会报错Traceback (most recent call last): ... ...ValueError: all the input array dimensions for the concatenation axis must match exactly, ...### 【5x04】numpy.stack()numpy.stack() 方法用于沿新轴连接数组序列。基本语法:numpy.stack(arrays, axis=0)参数解释:| 参数 | 描述 || —— | —— || arrays | 相同形状的数组序列 || axis | 返回数组中的轴,输入数组将沿着该轴来进行堆叠 |应用举例:12345678910>>> import numpy as np>>> a = np.array([1, 2, 3])>>> b = np.array([4, 5, 6])>>> print(np.stack((a, b),axis=0))[[1 2 3] [4 5 6]]>>> print(np.stack((a, b),axis=1))[[1 4] [2 5] [3 6]]123456789101112131415>>> import numpy as np>>> a = np.array([[1,2],[3,4]])>>> b = np.array([[5,6],[7,8]])>>> print(np.stack((a,b),axis=0))[[[1 2] [3 4]] [[5 6] [7 8]]]>>> print(np.stack((a,b),axis=1))[[[1 2] [5 6]] [[3 4] [7 8]]]### 【5x05】numpy.vstack() numpy.vstack() 方法通过垂直堆叠来生成数组。 基本语法:numpy.vstack(tup) 参数解释:tup:数组序列,如果是一维数组进行堆叠,则数组长度必须相同,其它数组除了第一个轴(axis=0)的长度可以不同外,其它轴的长度必须相同。 应用举例: 12345678910111213>>> import numpy as np>>> a = np.array([1, 2, 3])>>> b = np.array([4, 5, 6])>>> print(np.vstack((a,b)))[[1 2 3] [4 5 6]]>>> >>> b = np.array([4, 5])>>> print(np.vstack((a,b))) # 一维数组长度不一样时将抛出异常Traceback (most recent call last): ... ...ValueError: all the input array dimensions for the concatenation axis must match exactly, ... 123456789101112>>> import numpy as np>>> a = np.array([[1,2],[3,4]])>>> b = np.array([[5,6],[7,8]])>>> a.shape(2, 2)>>> b.shape(2, 2)>>> print(np.vstack((a,b)))[[1 2] [3 4] [5 6] [7 8]] 12345678910111213>>> import numpy as np>>> a = np.array([[1,2],[3,4]])>>> b = np.array([[5,6],[7,8],[9,10]])>>> a.shape(2, 2)>>> b.shape(3, 2)>>> print(np.vstack((a,b))) # 第一个轴(axis=0)可以不同,其它轴必须相同[[ 1 2] [ 3 4] [ 5 6] [ 7 8] [ 9 10]] 【5x06】numpy.hstack()numpy.hstack() 方法通过水平堆叠来生成数组。 基本语法:numpy.hstack(tup) 参数解释:tup:数组序列,除了一维数组的堆叠可以是不同长度外,其它数组堆叠时,除了第二个轴(axis=1)的长度可以不同外,其它轴的长度必须相同。 应用举例: 12345>>> import numpy as np>>> a = np.array([1, 2, 3])>>> b = np.array([4, 5])>>> print(np.hstack((a,b)))[1 2 3 4 5] 12345678910>>> import numpy as np>>> a = np.array([[1,2],[3,4]])>>> b = np.array([[5,6],[7,8]])>>> a.shape(2, 2)>>> b.shape(2, 2)>>> print(np.hstack((a,b)))[[1 2 5 6] [3 4 7 8]] 12345678910>>> import numpy as np>>> a = np.array([[1,2],[3,4]])>>> b = np.array([[5,6,7],[8,9,10]])>>> a.shape(2, 2)>>> b.shape(2, 3)>>> print(np.hstack((a,b))) # 第二个轴(axis=1)可以不同,其它轴必须相同[[ 1 2 5 6 7] [ 3 4 8 9 10]] 【5x07】numpy.dstack()numpy.dstack() 方法会沿着第三个维度拼接数组。 基本语法:numpy.dstack(tup) 参数解释:tup:数组序列,除了第三个轴(axis=2)的长度可以不同外,其它轴的长度必须相同。一维或二维数组必须具有相同的形状。 应用举例: 12345678910111213>>> import numpy as np>>> a = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])>>> b = np.array([[[9], [10]], [[11], [12]]])>>> a.shape(2, 2, 2)>>> b.shape(2, 2, 1)>>> print(np.dstack((a,b))) # a 与 b 的第三个轴(axis=2)一个是 2,另一个是 1,可以不同,但其他轴必须相同[[[ 1 2 9] [ 3 4 10]] [[ 5 6 11] [ 7 8 12]]] 123456789101112131415161718>>> import numpy as np>>> a = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])>>> b = np.array([[[9, 10], [11, 12]]])>>> a.shape(2, 2, 2)>>> b.shape(1, 2, 2)>>> print(np.dstack((a,b))) # a 与 b 的第一个轴(axis=0)不同,将会抛出异常[[[ 1 2 9] [ 3 4 10]] [[ 5 6 11] [ 7 8 12]]]>>> print(np.dstack((a,b)))Traceback (most recent call last): ... ...ValueError: all the input array dimensions for the concatenation axis must match exactly, ... 【5x08】以上几种方法的区别 concatenate() 方法在 axis=0 的时候相当于 vstack() 方法; concatenate() 方法在 axis=1 的时候相当于 hstack() 方法; concatenate() 方法在 axis=2 的时候相当于 dstack() 方法; concatenate() 方法不会生成一个新的维度,且数组的维度不一定相同,而 stack() 方法会生成一个新的维度,并且要求所有数组形状都要一样。 concatenate() 方法与 stack() 方法比较: 123456789101112131415161718>>> import numpy as np>>> a = np.array([1, 2, 3])>>> b = np.array([4, 5, 6])>>> c = np.stack((a, b), axis=0)>>> d = np.concatenate((a, b), axis=0)>>> a.shape(3,)>>> b.shape(3,)>>> c.shape(2, 3)>>> d.shape(6,)>>> print(c)[[1 2 3] [4 5 6]]>>> print(d)[1 2 3 4 5 6] 【5x09】numpy.insert()numpy.insert() 方法沿指定轴在指定索引之前插入值。 基本语法:numpy.insert(arr, obj, values, axis=None) 参数解释: 参数 描述 arr 原数组 obj 索引值,将在其之前插入值 values 要插入的值 axis 轴,将沿着该轴进行插入操作,如果未指定,则插入前,原数组会被展开,变为一维数组 应用举例: 12345678910111213>>> import numpy as np>>> a = np.array([[1,2],[3,4],[5,6]])>>> print(np.insert(a, 3, [0, 0])) # 未指定 axis 参数,在插入之前原数组会被展开[1 2 3 0 0 4 5 6]>>> print(np.insert(a, 1, [0], axis = 0)) # 沿 0 轴广播插入[[1 2] [0 0] [3 4] [5 6]]>>> print(np.insert(a, 1, 11, axis = 1)) # 沿 1 轴广播插入[[ 1 11 2] [ 3 11 4] [ 5 11 6]] 【5x10】numpy.r_numpy.r_:r 为 row(行) 的缩写,即按照行连接两个矩阵,要求列数相等。 应用举例: 123456789>>> import numpy as np>>> a = np.array([[1, 2, 3],[7, 8, 9]])>>> b = np.array([[4, 5, 6],[1, 2, 3]])>>> >>> np.r_[a, b]array([[1, 2, 3], [7, 8, 9], [4, 5, 6], [1, 2, 3]]) 【5x11】numpy.c_numpy.c_:c 为 column(列) 的缩写,即按照列连接两个矩阵,要求行数相等。 应用举例: 1234567>>> import numpy as np>>> a = np.array([[1, 2, 3],[7, 8, 9]])>>> b = np.array([[4, 5, 6],[1, 2, 3]])>>> >>> np.c_[a, b]array([[1, 2, 3, 4, 5, 6], [7, 8, 9, 1, 2, 3]]) 【6x00】数组的分割与元素的删除【6x01】numpy.split()numpy.split() 方法可以沿特定的轴将数组均等的分割为子数组。如果不能等分,将抛出异常。 基本语法:numpy.split(ary, indices_or_sections, axis=0) 参数解释: 参数 描述 ary 被分割的数组 indices_or_sections 如果是一个整数 N,则该数组将沿 axis 均分为 N 个数组如果是一维整数数组,则数组元素代表每个分割点位置(左闭右开),N 个分裂点会得到 N + 1 个子数组 axis 沿着哪个维度进行分割,默认为 0,横向分割;为 1 时,纵向分割 应用举例: 12345678910111213141516171819202122232425262728293031323334>>> import numpy as np>>> a = np.arange(10)>>> print(a)[0 1 2 3 4 5 6 7 8 9]>>> print(np.split(a, 5)) # 将数组分为五个大小相等的子数组[array([0, 1]), array([2, 3]), array([4, 5]), array([6, 7]), array([8, 9])]>>>>>> print(np.split(a, 4)) # 无法等分的情况下将抛出异常Traceback (most recent call last): ... ...ValueError: array split does not result in an equal division>>>>>> print(np.split(a, [4, 8])) # 分割点为索引 4 和 8 的位置,相当于 a[:4]、a[4:8]、a[8:][array([0, 1, 2, 3]), array([4, 5, 6, 7]), array([8, 9])]>>>>>> a = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]])>>> print(np.split(a, 5, axis=1)) # 指定 axis=1,按照纵向分割数组[array([[1], [6]]), array([[2], [7]]), array([[3], [8]]), array([[4], [9]]), array([[ 5], [10]])] 【6x02】numpy.array_split()numpy.array_split() 的用法和作用都与 split() 方法一致,都可以用一个整数或者整数列表来分割数组。 两者的区别:如果输入的是一个 int 类型的数字,那么在 split() 方法中,数组必须是均等的分割,否则就会报错,而在 array_split() 方法中是可以进行不均等的分割的。 基本语法:numpy.array_split(ary, indices_or_sections, axis=0) 参数解释: 参数 描述 ary 被分割的数组 indices_or_sections 如果是一个整数 N,则该数组将沿 axis 分割为 N 个数组,可以不是均分的如果是一维整数数组,则数组元素代表每个分割点位置(左闭右开),N 个分裂点会得到 N + 1 个子数组 axis 沿着哪个维度进行分割,默认为 0,横向分割;为 1 时,纵向分割 应用举例: 1234567>>> import numpy as np>>> a = np.array([1, 2, 3, 4, 5, 6, 7])>>> print(np.array_split(a, 3))[array([1, 2, 3]), array([4, 5]), array([6, 7])] # 可以是不均分的>>>>>> print(np.array_split(a, 4))[array([1, 2]), array([3, 4]), array([5, 6]), array([7])] 【6x03】numpy.vsplit()numpy.vsplit() 方法相当于 split() 方法在 axis=0 时的效果,即横向分割数组。 基本语法:numpy.vsplit(ary, indices_or_sections) 参数解释: 参数 描述 ary 被分割的数组 indices_or_sections 如果是一个整数 N,则该数组将沿 axis 分为 N 个相等的数组如果是一维整数数组,则数组元素代表每个分割点位置(左闭右开),N 个分裂点会得到 N + 1 个子数组 应用举例: 1234567891011121314>>> import numpy as np>>> a = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]])>>> a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]])>>> print(np.vsplit(a, 2))[array([[1, 2, 3], [4, 5, 6]]), array([[ 7, 8, 9], [10, 11, 12]])]>>>>>> print(np.vsplit(a, [1, 3]))[array([[1, 2, 3]]), array([[4, 5, 6], [7, 8, 9]]), array([[10, 11, 12]])] 【6x04】numpy.hsplit()numpy.hsplit() 方法相当于 split() 方法在 axis=1 时的效果,即纵向分割数组。 基本语法:numpy.hsplit(ary, indices_or_sections) 参数解释: 参数 描述 ary 被分割的数组 indices_or_sections 如果是一个整数 N,则该数组将沿 axis 分为 N 个相等的数组如果是一维整数数组,则数组元素代表每个分割点位置(左闭右开),N 个分裂点会得到 N + 1 个子数组 应用举例: 123456789101112131415161718192021>>> import numpy as np>>> a = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]])>>> print(np.hsplit(a, 5))[array([[1], [6]]), array([[2], [7]]), array([[3], [8]]), array([[4], [9]]), array([[ 5], [10]])]>>>>>> print(np.hsplit(a, [1, 3]))[array([[1], [6]]), array([[2, 3], [7, 8]]), array([[ 4, 5], [ 9, 10]])] 【6x05】numpy.dsplit()numpy.dsplit() 方法相当于 split() 方法在 axis=2 时的效果,即沿第三轴将数组拆分为多个子数组。 基本语法:numpy.dsplit(ary, indices_or_sections) 参数解释: 参数 描述 ary 被分割的数组 indices_or_sections 如果是一个整数 N,则该数组将沿 axis 分为 N 个相等的数组如果是一维整数数组,则数组元素代表每个分割点位置(左闭右开),N 个分裂点会得到 N + 1 个子数组 应用举例: 12345678910111213141516171819202122232425262728>>> import numpy as np>>> a = np.arange(16).reshape(2, 2, 4)>>> print(a)[[[ 0 1 2 3] [ 4 5 6 7]] [[ 8 9 10 11] [12 13 14 15]]]>>> print(np.dsplit(a, 2))[array([[[ 0, 1], [ 4, 5]], [[ 8, 9], [12, 13]]]), array([[[ 2, 3], [ 6, 7]], [[10, 11], [14, 15]]])]>>>>>> print(np.dsplit(a, [3, 6]))[array([[[ 0, 1, 2], [ 4, 5, 6]], [[ 8, 9, 10], [12, 13, 14]]]), array([[[ 3], [ 7]], [[11], [15]]]), array([], shape=(2, 2, 0), dtype=int32)] 【6x06】numpy.delete()numpy.delete() 方法返回一个从原数组中删除了指定子数组的新数组。 基本语法:numpy.delete(arr,obj,axis=None) 参数解释: 参数 描述 arr 原数组 obj 可以是切片、整数或整数数组形式,表示沿指定轴删除的子数组的索引当 obj 为切片形式时,要用 np.s_[:] 的格式 axis 轴,将沿着该轴进行插入操作,如果未指定,则插入前,原数组会被展开,变为一维数组 应用举例: 1234567891011121314151617181920212223>>> import numpy as np>>> a = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])>>> print(np.delete(a, 8)) # 未指定 axis 参数,在插入之前原数组会被展开,然后再删除索引为 8 的元素[ 1 2 3 4 5 6 7 8 10 11 12]>>>>>> print(np.delete(a, 1, axis=0)) # 指定 axis=0,删除索引值为 1 即第二行[[ 1 2 3 4] [ 9 10 11 12]]>>>>>> print(np.delete(a, 1, axis=1)) # 指定 axis=1,删除索引值为 1 即第二列[[ 1 3 4] [ 5 7 8] [ 9 11 12]]>>>>>> print(np.delete(a, np.s_[:2], axis=1)) # 切片形式,删除前两列[[ 3 4] [ 7 8] [11 12]]>>>>>> print(np.delete(a, [0, 2], axis=1)) # 数组形式,删除索引值为 0 和 2 的列[[ 2 4] [ 6 8] [10 12]] 【6x07】numpy.unique()numpy.unique() 方法用于去除数组中的重复元素。 基本语法:numpy.unique(arr, return_index=False, return_inverse=False, return_counts=False, axis=None) 参数解释: 参数 描述 arr 原数组,如果不是一维数组则会被展开为一维数组 return_index 如果为 true,则返回新列表元素在旧列表中的位置(下标),并以列表形式储 return_inverse 如果为 true,则返回旧列表元素在新列表中的位置(下标),并以列表形式储 return_counts 如果为 true,则返回去重数组中的元素在原数组中的出现次数 axis 指定轴 应用举例: 未指定 axis 值,原数组将会被展开: 1234567>>> import numpy as np>>> a = np.array([1, 1, 2, 2, 3, 4, 5, 5])>>> b = np.array([[1, 1], [2, 3], [3, 4]])>>> print(np.unique(a))[1 2 3 4 5]>>> print(np.unique(b))[1 2 3 4] 指定 axis 值: 12345678910111213141516>>> import numpy as np>>> a = np.array([[1, 0, 1], [1, 0, 1], [2, 3, 2], [5, 6, 5]])>>> print(a)[[1 0 1] [1 0 1] [2 3 2] [5 6 5]]>>> print(np.unique(a, axis=0)) # 删除相同的行[[1 0 1] [2 3 2] [5 6 5]]>>> print(np.unique(a, axis=1)) # 删除相同的列[[0 1] [0 1] [3 2] [6 5]] return_counts 为 True 时,返回去重数组中的元素在原数组中的出现次数: 12345>>> import numpy as np>>> a = np.array([1, 1, 1, 2, 2, 3, 4, 5, 5])>>> print(np.unique(a, return_counts=True))(array([1, 2, 3, 4, 5]), array([3, 2, 1, 1, 2], dtype=int64))# 前一个 array 表示去重后的数组,后一个 array 表示每一个元素在原数组中出现的次数 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/104988137未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"NumPy","slug":"Python-数据分析/NumPy","permalink":"https://www.itrhx.com/categories/Python-数据分析/NumPy/"}],"tags":[{"name":"NumPy","slug":"NumPy","permalink":"https://www.itrhx.com/tags/NumPy/"},{"name":"数组","slug":"数组","permalink":"https://www.itrhx.com/tags/数组/"},{"name":"索引","slug":"索引","permalink":"https://www.itrhx.com/tags/索引/"},{"name":"切片","slug":"切片","permalink":"https://www.itrhx.com/tags/切片/"},{"name":"广播","slug":"广播","permalink":"https://www.itrhx.com/tags/广播/"},{"name":"拼接","slug":"拼接","permalink":"https://www.itrhx.com/tags/拼接/"},{"name":"分割","slug":"分割","permalink":"https://www.itrhx.com/tags/分割/"}]},{"title":"Python 数据分析三剑客之 NumPy(一):理解 NumPy / 数组基础","slug":"A62-NumPy-01","date":"2020-03-20T05:15:50.677Z","updated":"2020-08-06T03:01:29.975Z","comments":true,"path":"2020/03/20/A62-NumPy-01/","link":"","permalink":"https://www.itrhx.com/2020/03/20/A62-NumPy-01/","excerpt":"","text":"NumPy 系列文章: Python 数据分析三剑客之 NumPy(一):理解 NumPy / 数组基础 Python 数据分析三剑客之 NumPy(二):数组索引 / 切片 / 广播 / 拼接 / 分割 Python 数据分析三剑客之 NumPy(三):数组的迭代与位运算 Python 数据分析三剑客之 NumPy(四):字符串函数总结与对比 Python 数据分析三剑客之 NumPy(五):数学 / 算术 / 统计 / 排序 / 条件 / 判断函数合集 Python 数据分析三剑客之 NumPy(六):矩阵 / 线性代数库与 IO 操作 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/104870084未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【1x00】了解 NumPyNumPy 是使用 Python 进行科学计算的基础包,支持大量的维度数组与矩阵运算,对数组运算提供大量的数学函数库。NumPy 重在数值计算,是大部分 Python 科学计算库的基础库,多用于在大型、多维数组上执行数值运算。 NumPy 主要包含如下的内容: 一个强大的 N 维数组对象(Ndarray); 复杂的广播功能函数; 集成 C/C++/Fortran 代码的工具; 具有线性代数、傅里叶变换、随机数生成等功能。 【2x00】NumPy 数组与 Python 列表的区别Numpy 使用 Ndarray 对象来处理多维数组,Python 列表通常存储一维数组,通过列表的嵌套也可以实现多维数组。 Numpy 的 Ndarray 对象是一个快速而灵活的大数据容器。Numpy 专门针对数组的操作和运算进行了设计,所以数组的存储效率和输入输出性能远优于 Python 中的嵌套列表,数组越大,Numpy 的优势就越明显。 通常 Numpy 数组中的所有元素的类型都是相同的,而 Python 列表中的元素类型是任意的,所以在通用性能方面 Numpy 数组不及 Python 列表,但在科学计算中,可以省掉很多循环语句,代码使用方面比 Python 列表简单的多。 Python 列表的元素不同类型举例: 123>>> l = [True, '2', 3.2, 5]>>> [type(item) for item in l][<class 'bool'>, <class 'str'>, <class 'float'>, <class 'int'>] Python 列表中的每一项必须包含各自的类型信息、引用计数和其他信息,也就是说,每一项都是一个完整的 Python 对象,同时,Python 列表还包含一个指向指针块的指针,其中的每一个指针对应一个完整的 Python 对象,另外,列表的优势是灵活,因为每个列表元素是一个包含数据和类型信息的完整结构体。相反 NumPy 数组缺乏这种灵活性,但是 NumPy 却能更有效地存储和操作数据。 【3x00】理解 NumPy Ndarray 对象NumPy 提供了一个 N 维数组类型,即 Ndarray,它是一系列同类型数据的集合,是用于存放同类型元素的多维数组,以 0 下标为开始进行集合中元素的索引,所有 Ndarray 中的每个元素在内存中都有相同存储大小的区域。 Ndarray 内部由以下内容组成: 一个指向数据(内存或内存映射文件中的一块数据)的指针; 数据类型或 dtype,描述在数组中的固定大小值的格子; 一个表示数组形状(shape)的元组,表示各维度大小的元组; 一个跨度元组(stride),其中的整数指的是为了前进到当前维度下一个元素需要“跨过”的字节数。 【4x00】理解不同维度的数组NumPy 数组的维数称为秩(rank),秩就是轴的数量,即数组的维度,一维数组的秩为 1,二维数组的秩为 2,以此类推。 在 NumPy 中,每一个线性的数组称为是一个轴(axis),也就是维度(dimensions)。比如说,二维数组相当于是两个一维数组,其中第一个一维数组中每个元素又是一个一维数组。所以一维数组就是 NumPy 中的轴(axis),第一个轴相当于是底层数组,第二个轴是底层数组里的数组。而轴的数量 — 秩,就是数组的维数。 很多时候可以声明 axis,axis=0,表示沿着第 0 轴进行操作,即对每一列进行操作;axis=1,表示沿着第 1 轴进行操作,即对每一行进行操作。 一维数组: 123456>>> import numpy as np>>> a = np.array([1, 2, 3, 4])>>> print(a)[1 2 3 4]>>> print(a.shape)(4,) 二维数组: 12345678>>> import numpy as np>>> a = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])>>> print(a)[[1 2 3 4] [5 6 7 8]]>>> print(a.shape)(2, 4) a.shape 输出数组的维度,对于此二维数组,可以理解为 2 行 4 列。 三维数组: 1234567891011121314>>> import numpy as np>>> a = np.array([[[1,2,3,4], [5,6,7,8]], [[9,10,11,12], [13,14,15,16]], [[17,18,19,20], [21,22,23,24]]])>>> print(a)[[[ 1 2 3 4] [ 5 6 7 8]] [[ 9 10 11 12] [13 14 15 16]] [[17 18 19 20] [21 22 23 24]]]>>> print(a.shape)(3, 2, 4) a.shape 输出数组的维度,对于此三维数组,可以理解为 3 块,每块有 2 行 4 列。 有网友对三维数组的这个图有疑问,认为横线应该是 axis=0,竖线是 axis=1,斜线是 axis=2,这个确实有点儿绕,不要受到前面一维二维的影响,我把我的理解又画了一张图出来,另外大家可以尝试去取三维数组里面的某个值,多想一下就可以理解了。欢迎各位大佬一起交流学习! 【5x00】创建 Ndarray 对象(创建数组)【5x01】一张表快速了解创建数组的不同方法 方法 描述 numpy.array() 将输入数据(列表、元组、Ndarray 等)转换为数组形式当数据源为 Ndarray 时,该方法仍然会 copy 出一个副本,占用新的内存 numpy.asarray() 将输入数据(列表、元组、Ndarray 等)转换为数组形式当数据源为 Ndarray 时,该方法不会 copy 出一个副本,不占用新的内存 numpy.arange() 创建一个一维数组,该数组由一个等差数列构成通过指定开始值、终值和步长创建等差数列,得到的结果数组不包含终值 numpy.linspace() 创建一个一维数组,该数组由一个等差数列构成通过指定开始值、终值和元素个数创建等差数列,可通过 endpoint 参数指定是否包含终值 numpy.logspace() 创建一个一维数组,该数组由一个等比数列构成 numpy.empty() 创建一个指定形状、数据类型且未初始化的数组 numpy.zeros() 创建一个指定大小的数组,数组元素以 0 来填充 numpy.ones() 创建一个指定大小的数组,数组元素以 1 来填充 numpy.eye() 创建一个对角矩阵数组,返回一个二维数组,对角线上值为 1,其余位置为 0 numpy.frombuffer() 将缓冲区解释为一维数组,接受 buffer 输入参数,以流的形式读入并转化成 Ndarray 对象 numpy.fromiter() 从可迭代对象中建立 Ndarray 对象,返回一个一维数组 【5x02】numpy.array()调用 NumPy 的 array 方法即可创建一个 Ndarray 对象,即创建一个数组。 基本语法:numpy.array(object, dtype = None, copy = True, order = None, subok = False, ndmin = 0) 参数解释: 参数 描述 object 数组或嵌套的数列 dtype 数组元素的数据类型,可选 copy 对象是否需要复制,可选 order 创建数组的样式,C为行方向,F为列方向,A为任意方向(默认) subok 默认返回一个与基类类型一致的数组 ndmin 指定生成数组的最小维度 创建一个一维数组: 123456>>> import numpy as np>>> a = np.array([1, 2, 3])>>> print(a)[1 2 3]>>> print(type(a))<class 'numpy.ndarray'> 创建一个二维数组: 1234567>>> import numpy as np>>> a = np.array([[1, 2, 3], [4, 5, 6]])>>> print(a)[[1 2 3] [4 5 6]]>>> print(type(a))<class 'numpy.ndarray'> 创建一个三维数组: 12345678>>> import numpy as np>>> a = np.array([[[1,2,3], [4,5,6]], [[7,8,9], [10,11,12]]])>>> print(a)[[[ 1 2 3] [ 4 5 6]] [[ 7 8 9] [10 11 12]]] 【5x03】numpy.asarray()numpy.asarray() 方法将输入数据(列表、元组、Ndarray 等)转换为数组形式,与 numpy.array() 方法类似,但 asarray 参数比 array 少两个,另外最大的区别是当数据源为 Ndarray 时,array 方法仍然会 copy 出一个副本,占用新的内存,但 asarray 方法不会。 基本语法:numpy.asarray(a, dtype=None, order=None) 参数解释: 参数 描述 a 待转换对象,可以是列表,元组,列表元组,元组列表,多维数组等 dtype 可选项,指定数据类型 order 可选项,以行优先(C)或列优先(F)的顺序存储多维数据在内存中 将列表转换为 Ndarray: 1234567>>> import numpy as np>>> l = [1,2,3,4]>>> n = np.asarray(l)>>> print(n)[1 2 3 4]>>> print(type(n))<class 'numpy.ndarray'> 将元组转换为 Ndarray: 1234567>>> import numpy as np>>> l = (1,2,3,4)>>> n = np.asarray(l)>>> print(n)[1 2 3 4]>>> print(type(n))<class 'numpy.ndarray'> 将元组列表转换为 Ndarray: 1234567>>> import numpy as np>>> l = [(1,2,3),(4,5)]>>> n = np.asarray(l)>>> print(n)[(1, 2, 3) (4, 5)]>>> print(type(n))<class 'numpy.ndarray'> 指定 dtype 参数: 1234567>>> import numpy as np>>> l = [1,2,3]>>> n = np.asarray(l, dtype=float)>>> print(n)[1. 2. 3.]>>> print(type(n))<class 'numpy.ndarray'> numpy.asarray() 方法和 numpy.array() 的区别演示: 当输入数据为列表、元组等格式时,两者没有区别,都可以将其转为数组格式: 1234567891011121314151617>>> import numpy as np>>> a = [[1,2,3], [4,5,6], [7,8,9]]>>> b = np.array(a)>>> c = np.asarray(a)>>> a[1] = 0>>> print(a)[[1, 2, 3], 0, [7, 8, 9]]>>> print(type(a)) # a 为列表<class 'list'>>>> print(b) # 列表对象 a 的值改变,array 方法得到的值不会改变[[1 2 3] [4 5 6] [7 8 9]]>>> print(c) # 列表对象 a 的值改变,asarray 方法得到的值不会改变[[1 2 3] [4 5 6] [7 8 9]] 当输入数据为 Ndarray 时,array 方法仍然会 copy 出一个副本,占用新的内存,但 asarray 方法不会: 12345678910111213141516171819>>> import numpy as np>>> a = np.ones((3,3))>>> b = np.array(a)>>> c = np.asarray(a)>>> a[1][1] = 2>>> print(a)[[1. 1. 1.] [1. 2. 1.] [1. 1. 1.]]>>> print(type(a)) # a 为 Ndarray 对象<class 'numpy.ndarray'>>>> print(b) # Ndarray 对象 a 的值改变,array 方法得到的值不会改变[[1. 1. 1.] [1. 1. 1.] [1. 1. 1.]]>>> print(c) # Ndarray 对象 a 的值改变,asarray 方法得到的值也将改变[[1. 1. 1.] [1. 2. 1.] [1. 1. 1.]] 【5x04】numpy.arange()numpy.arange() 方法用于创建一个一维数组,在指定的间隔内返回均匀间隔的数字并组成数组(Ndarray 对象),即该数组是一个等差数列构成的。arange() 类似 Python 的 range(),但是 arange() 的步长可以为小数,而 range() 的步长只能是整数。 基本语法:numpy.arange([start, ]stop, [step, ]dtype=None) 参数解释: 参数 描述 start 起始值,数字,可选项,默认起始值为 0,生成的元素包括起始值 stop 结束值,数字,生成的元素不包括结束值 step 步长,数字,可选项, 默认步长为 1,如果指定了 step,则必须给出 start dtype 输出数组的类型,如果未给出 dtype,则从其他输入参数推断数据类型 应用举例: 12345678>>> import numpy as np>>> a = np.arange(5) # 相当于 np.array([0, 1, 2, 3, 4])>>> b = np.arange(2, 5) # 相当于 np.array([2, 3, 4])>>> c = np.arange(2, 9, 3)>>> print('a = %s\\nb = %s\\nc = %s' %(a,b,c))a = [0 1 2 3 4]b = [2 3 4]c = [2 5 8] 【5x05】numpy.linspace()numpy.linspace() 方法用于创建一个一维数组,在指定的间隔内返回均匀间隔的数字并组成数组(Ndarray 对象),即该数组是一个等差数列构成的。linspace() 方法类似于 arange(),两者除了参数有差别以外,还有以下的区别: arange() 方法类似于内置函数 range(),通过指定开始值、终值和步长创建表示等差数列的一维数组,得到的结果数组不包含终值。 linspace() 通过指定开始值、终值和元素个数创建表示等差数列的一维数组,可以通过 endpoint 参数指定是否包含终值,默认值为True,即包含终值。 基本语法:numpy.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None, axis=0) 参数解释: 参数 描述 start 序列的起始值 stop 序列的终止值,如果 endpoint 为 True,则该值将包含于数列中 num 可选项,int 类型,要生成的等步长的样本数量,即元素个数,默认为 50 endpoint 可选项,bool 类型,该值为 True 时,数列中将包含 stop 值,反之则不包含,默认为 True retstep 可选项,bool 类型,该值为 True 时,生成的数组中会显示间距,反之则不显示,默认为 False dtype 可选项,Ndarray 的数据类型 axis 可选项,int 类型,结果中的轴用于存储样本。仅当 start 或 stop 类似于数组时才相关默认情况下为 0,采样将沿着在开始处插入的新轴进行,使用 -1 来获得轴的末端 应用举例: 不指定 num 值,将默认生成 50 个元素,数列中将包含 stop 值: 123456789101112>>> import numpy as np>>> a = np.linspace(1, 10)>>> print(a)[ 1. 1.18367347 1.36734694 1.55102041 1.73469388 1.91836735 2.10204082 2.28571429 2.46938776 2.65306122 2.83673469 3.02040816 3.20408163 3.3877551 3.57142857 3.75510204 3.93877551 4.12244898 4.30612245 4.48979592 4.67346939 4.85714286 5.04081633 5.2244898 5.40816327 5.59183673 5.7755102 5.95918367 6.14285714 6.32653061 6.51020408 6.69387755 6.87755102 7.06122449 7.24489796 7.42857143 7.6122449 7.79591837 7.97959184 8.16326531 8.34693878 8.53061224 8.71428571 8.89795918 9.08163265 9.26530612 9.44897959 9.63265306 9.81632653 10. ] 指定 num 值为 10,将生成 10 个元素,数列中将包含 stop 值: 1234>>> import numpy as np>>> a = np.linspace(1, 10, 10)>>> print(a)[ 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.] 指定 endpoint 值为 False,retstep 值为 True,数列中不包含 stop 值,生成的数组中会显示间距: 1234>>> import numpy as np>>> a = np.linspace(1, 10, 10, endpoint=False, retstep=True)>>> print(a)(array([1. , 1.9, 2.8, 3.7, 4.6, 5.5, 6.4, 7.3, 8.2, 9.1]), 0.9) 指定 dtype 类型为 int: 1234>>> import numpy as np>>> a = np.linspace(1, 10, 10, endpoint=False, retstep=True, dtype=int)>>> print(a)(array([1, 1, 2, 3, 4, 5, 6, 7, 8, 9]), 0.9) 【5x06】numpy.logspace()numpy.logspace() 方法用于创建一个一维数组,该数组由一个等比数列构成。 基本语法:numpy.logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None, axis=0) 参数解释: 参数 描述 start 序列的起始值 stop 序列的终止值,如果 endpoint 为 True,则该值将包含于数列中 num 可选项,int 类型,要生成的等步长的样本数量,即元素个数,默认为 50 endpoint 可选项,bool 类型,该值为 True 时,数列中将包含 stop 值,反之则不包含,默认为 True base 可选项,float 类型,对数 log 的底数,即取对数的时候 log 的下标 ,默认为 10.0 dtype 可选项,Ndarray 的数据类型 axis 可选项,int 类型,结果中的轴用于存储样本。仅当 start 或 stop 类似于数组时才相关默认情况下为 0,采样将沿着在开始处插入的新轴进行,使用 -1 来获得轴的末端 应用举例: 指定起始值为 0,,终止值为 9,base 默认值为 10,代表的是 10 的幂,即 0 代表 10 的 0 次方,9 代表 10 的 9 次方: 1234>>> import numpy as np>>> a = np.logspace(0, 9, num = 10)>>> print(a)[1.e+00 1.e+01 1.e+02 1.e+03 1.e+04 1.e+05 1.e+06 1.e+07 1.e+08 1.e+09] 指定起始值为 0,,终止值为 9,base 值为 2,代表的是 2 的幂,即 0 代表 2 的 0 次方,9 代表 2 的 9 次方: 1234>>> import numpy as np>>> a = np.logspace(0, 9, num = 10, base = 2)>>> print(a)[ 1. 2. 4. 8. 16. 32. 64. 128. 256. 512.] 起始值和终止值都可以为 float 类型: 12345>>> import numpy as np>>> a = np.logspace(1.0, 2.0, num = 10)>>> print(a)[ 10. 12.91549665 16.68100537 21.5443469 27.82559402 35.93813664 46.41588834 59.94842503 77.42636827 100. ] 定义 dtype 属性值为 int 类型: 1234>>> import numpy as np >>> a = np.logspace(0.0, 9.0, num = 10, base = 2, dtype = int)>>> print(a)[ 1 2 4 8 16 32 64 128 256 512] 【5x07】numpy.empty()numpy.empty() 方法可用来创建一个指定形状(shape)、数据类型(dtype)且未初始化的数组。 基本语法:numpy.empty(shape, dtype = float, order = 'C') 参数解释: 参数 描述 shape 数组形状 dtype 数据类型,可选 order 以行优先(C)或列优先(F)的顺序存储多维数据在内存中 创建一个一维空数组(传递一个参数即可,代表数组长度,数组元素为随机值,因为它们未初始化): 12345678910>>> import numpy as np>>> a = np.empty(3)>>> print(a)[3.538e-321 3.538e-321 0.000e+000]>>> print(type(a))<class 'numpy.ndarray'>>>>>>> a = np.empty(3, dtype = int) # 定义类型为整数>>> print(a)[716 0 716] 创建一个二维空数组(传递两个参数,分别代表行数和列数): 12345678>>> import numpy as np>>> a = np.empty([3, 2])>>> print(a)[[6.23042070e-307 3.56043053e-307] [1.37961641e-306 1.11258854e-306] [8.90100843e-307 1.11261027e-306]]>>> print(type(a))<class 'numpy.ndarray'> 创建一个三维空数组(传递三个参数,分别代表块数、每一块的行数和列数): 1234567891011>>> import numpy as np>>> a = np.empty([3, 2, 4])>>> print(a)[[[0. 0. 0. 0.] [0. 0. 0. 0.]] [[0. 0. 0. 0.] [0. 0. 0. 0.]] [[0. 0. 0. 0.] [0. 0. 0. 0.]]] 【5x08】numpy.zeros()numpy.zeros() 方法用于创建指定大小的数组,数组元素以 0 来填充。 基本语法:numpy.zeros(shape, dtype = float, order = 'C') 参数解释: 参数 描述 shape 数组形状 dtype 数据类型,可选 order 以行优先(C)或列优先(F)的顺序存储多维数据在内存中 创建一个一维数组(传递一个参数即可,代表数组长度,数组元素以 0 填充): 12345678910>>> import numpy as np>>> a = np.zeros(5)>>> print(a)[0. 0. 0. 0. 0.]>>> print(type(a))<class 'numpy.ndarray'>>>>>>> a = np.zeros(5, dtype = int) # 定义类型为整数>>> print(a)[0 0 0 0 0] 创建一个二维数组(传递两个参数,分别代表行数和列数): 1234567>>> import numpy as np>>> a = np.zeros([2, 3])>>> print(a)[[0. 0. 0.] [0. 0. 0.]]>>> print(type(a))<class 'numpy.ndarray'> 创建一个三维空数组(传递三个参数,分别代表块数、每一块的行数和列数): 12345678910111213141516>>> import numpy as np>>> a = np.zeros([4, 2, 3])>>> print(a)[[[0. 0. 0.] [0. 0. 0.]] [[0. 0. 0.] [0. 0. 0.]] [[0. 0. 0.] [0. 0. 0.]] [[0. 0. 0.] [0. 0. 0.]]]>>> print(type(a))<class 'numpy.ndarray'> 【5x09】numpy.ones()numpy.ones() 方法用于创建指定大小的数组,数组元素以 1 来填充。 基本语法:numpy.ones(shape, dtype = None, order = 'C') 参数解释: 参数 描述 shape 数组形状 dtype 数据类型,可选 order 以行优先(C)或列优先(F)的顺序存储多维数据在内存中 创建一个一维数组(传递一个参数即可,代表数组长度,数组元素以 0 填充): 123456789101112>>> import numpy as np>>> a = np.ones(5)>>> print(a)[1. 1. 1. 1. 1.]>>> print(type(a))<class 'numpy.ndarray'>>>> >>> a = np.ones(5, dtype = int) # 定义类型为整数>>> print(a)[1 1 1 1 1]>>> print(type(a))<class 'numpy.ndarray'> 创建一个二维数组(传递两个参数,分别代表行数和列数): 1234567>>> import numpy as np>>> a = np.ones([2, 3])>>> print(a)[[1. 1. 1.] [1. 1. 1.]]>>> print(type(a))<class 'numpy.ndarray'> 创建一个三维数组(传递三个参数,分别代表块数、每一块的行数和列数): 12345678910111213>>> import numpy as np>>> a = np.ones([3, 2 ,5])>>> print(a)[[[1. 1. 1. 1. 1.] [1. 1. 1. 1. 1.]] [[1. 1. 1. 1. 1.] [1. 1. 1. 1. 1.]] [[1. 1. 1. 1. 1.] [1. 1. 1. 1. 1.]]]>>> print(type(a))<class 'numpy.ndarray'> 【5x10】numpy.eye()numpy.eye() 方法用于创建对角矩阵数组,返回一个二维数组,对角线上值为 1,其余位置为 0。 基本语法:numpy.eye(N, M=None, k=0, dtype=<class 'float'>, order='C') 参数解释: 参数 描述 N int 类型,目标数组的行数 M int 类型,可选项,目标数组的列数,如果未指定,则默认与行数(N)相同 k int 类型,可选项,对角线索引,0(默认值)为主对角线,正值为上对角线,负值为下对角线简单来说可以理解成将值为 1 的对角线向左右平移 k 个单位,默认值 0 即对角线为 1,k 为正值右移,负值左移 dtype 可选项,返回数组的数据类型 order 可选项,以行优先(C)或列优先(F)的顺序存储多维数据在内存中 应用举例: 12345678910111213141516171819202122232425>>> import numpy as np>>> print(np.eye(5, 5)) # 创建一个对角矩阵[[1. 0. 0. 0. 0.] [0. 1. 0. 0. 0.] [0. 0. 1. 0. 0.] [0. 0. 0. 1. 0.] [0. 0. 0. 0. 1.]]>>> print(np.eye(5, 5, k=1)) # 将值为 1 的对角线向右移 1 个单位[[0. 1. 0. 0. 0.] [0. 0. 1. 0. 0.] [0. 0. 0. 1. 0.] [0. 0. 0. 0. 1.] [0. 0. 0. 0. 0.]]>>> print(np.eye(5, 5, k=-2)) # 将值为 1 的对角线向右左移 2 个单位[[0. 0. 0. 0. 0.] [0. 0. 0. 0. 0.] [1. 0. 0. 0. 0.] [0. 1. 0. 0. 0.] [0. 0. 1. 0. 0.]]>>> print(np.eye(5, dtype=int)) # 指定为 int 类型[[1 0 0 0 0] [0 1 0 0 0] [0 0 1 0 0] [0 0 0 1 0] [0 0 0 0 1]] 【5x11】numpy.frombuffer()numpy.frombuffer() 方法将缓冲区解释为一维数组,接受 buffer 输入参数,以流的形式读入转化成 ndarray 对象。当 buffer 是字符串时,Python3 默认 str 是 Unicode 类型,所以要转成 bytestring,即在原 str 前加上 b。 基本语法:numpy.frombuffer(buffer, dtype=float, count=-1, offset=0) 参数解释: 参数 描述 buffer 可以是任意对象,会以流的形式读入 dtype 可选项,返回数组的数据类型 count 可选项,读取的数据数量,默认为 -1,即读取缓冲区中所有数据 offset 可选项,读取的起始位置,以字节为单位,默认为 0 应用举例: 12345678910111213141516171819>>> import numpy as np>>> a = b'I love python!'>>> b = np.frombuffer(a, dtype='S1')>>> print(b)[b'I' b' ' b'l' b'o' b'v' b'e' b' ' b'p' b'y' b't' b'h' b'o' b'n' b'!']>>> >>> b = np.frombuffer(a, dtype='S1', count=5) # 指定要读取的数据量>>> print(b)[b'I' b' ' b'l' b'o' b'v']>>> >>> b = np.frombuffer(a, dtype='S1', count=5, offset=6) # 指定读取数据的起始位置>>> print(b)[b' ' b'p' b'y' b't' b'h']>>>>>> import numpy as np>>> a = b'\\x01\\x02'>>> b = np.frombuffer(a, dtype='uint8')>>> print(b)[1 2] 【5x12】numpy.fromiter()numpy.fromiter() 方法可以从可迭代对象中建立 Ndarray 对象,返回一个一维数组。 基本语法:numpy.fromiter(iterable, dtype, count=-1) 参数解释: 参数 描述 iterable 可迭代对象 dtype 返回数组的数据类型 count 读取的数据数量,默认为 -1,即读取所有数据 应用举例: 12345678910>>> import numpy as np>>> l = range(5)>>> i = iter(l) # iter() 方法用于生成迭代器>>> n = np.fromiter(i, dtype=float) # 从可迭代对象中建立 Ndarray 对象>>> print(l, type(l))range(0, 5) <class 'range'>>>> print(i, type(i))<range_iterator object at 0x00000163E75DCA70> <class 'range_iterator'>>>> print(n, type(n))[0. 1. 2. 3. 4.] <class 'numpy.ndarray'> 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/104870084未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【6x00】改变数组的维度或者形状【6x01】numpy.reshape()numpy.reshape() 方法用于重新调整数组的维数(重塑)。 基本语法:numpy.reshape(a, newshape, order='C') 参数解释: a:要重塑的数组newshape:重塑后的形状,新形状应与原始形状兼容。如果是整数,则结果将是该长度的一维数组。一个形状维度可以是-1。在这种情况下,将根据数组的长度和剩余维度推断该值。举个例子,原数组 a 是一个 4 行 n 列的二维数组,现在要将其转换成只有 1 行的一维数组,由于不清楚原二维数组有多少列,也就不清楚一共有多少元素,所以可以使用 np.reshape(a, (1, -1)) 语句将其转化为一维数组,其中 -1 会让程序自动计算有多少列,此概念将在后面举例具体说明。order:可选值为 C、F、A,使用索引顺序读取 a 的元素,并按照索引顺序将元素放到变换后的的数组中,默认参数为 C。C 指的是用类 C 写的读/索引顺序的元素,最后一个维度变化最快,第一个维度变化最慢。横着读,横着写,优先读/写一行。F 是指用 FORTRAN 类索引顺序读/写元素,最后一个维度变化最慢,第一个维度变化最快。竖着读,竖着写,优先读/写一列。注意,C 和 F 选项不考虑底层数组的内存布局,只引用索引的顺序。A 选项所生成的数组的效果与原数组 a 的数据存储方式有关,如果数据是按照 FORTRAN 存储的话,它的生成效果与 F 相同,否则与 C 相同。应用举例:123456789101112131415>>> import numpy as np>>> a = np.array([1,2,3,4,5,6,7,8]) # 创建一个一维数组>>> print(a)[1 2 3 4 5 6 7 8]>>> b = np.reshape(a, (2,4)) # 重塑为一个二维数组>>> print(b)[[1 2 3 4] [5 6 7 8]]>>> c = np.reshape(a, (2,2,2)) # 重塑为一个三维数组>>> print(c)[[[1 2] [3 4]] [[5 6] [7 8]]]添加 order 参数举例:1234567891011>>> import numpy as np>>> a = np.array([[1,2,3], [4,5,6]]) # 创建一个二维数组>>> print(a)[[1 2 3] [4 5 6]]>>> b = np.reshape(a, 6, order='C') # 按照行优先>>> print(b)[1 2 3 4 5 6]>>> b = np.reshape(a, 6, order='F') # 按照列优先>>> print(b)[1 4 2 5 3 6]另外,reshape 方法新生成的数组和原数组共用一个内存,不管改变哪个都会互相影响:1234567891011121314>>> import numpy as np>>> a = np.array([1,2,3,4,5,6,7,8])>>> b = np.reshape(a, (2,4))>>> print(a)[1 2 3 4 5 6 7 8]>>> print(b)[[1 2 3 4] [5 6 7 8]]>>> a[0] = 666>>> print(a)[666 2 3 4 5 6 7 8]>>> print(b)[[666 2 3 4] [ 5 6 7 8]]newshape 重塑后的形状维度可以是 -1,简单举例:- reshape(1,-1):将原数组转化成一行 N 列- reshape(2,-1):将原数组转换成两行 N 列- reshape(-1,1):将原数组转换成一列 N 行- reshape(-1,2):将原数组转化成两列 N 行123456789101112131415161718192021222324>>> import numpy as np>>> a = np.arange(16) # 生成一个由 0-15 组成的一维数组>>> print(a)[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]>>> b = np.reshape(a, (2,8)) # 将一维数组 a 转换成一个 2 行 8 列的二维数组 b>>> print(b)[[ 0 1 2 3 4 5 6 7] [ 8 9 10 11 12 13 14 15]]>>> c = np.reshape(b, (8,-1)) # 将二维数组 b 转换成 8 行的格式,程序自动计算列数(列数:16/8=2)>>> print(c)[[ 0 1] [ 2 3] [ 4 5] [ 6 7] [ 8 9] [10 11] [12 13] [14 15]]>>> d = np.reshape(c, (-1,4)) # 将二维数组 c 转换成 4 列的格式,程序自动计算行数(行数:16/4=4)>>> print(d)[[ 0 1 2 3] [ 4 5 6 7] [ 8 9 10 11] [12 13 14 15]]### 【6x02】numpy.ravel() numpy.ravel() 方法用于完成展平的操作。 基本语法:numpy.ravel(a, order='C') 参数解释: 参数 描述 a 待转换的数组 order 值可以是 C F A K,含义与 reshape 方法中参数的一样,与 reshape 方法不同的是多了个值 KK 表示按顺序在内存中读取元素,但在跨距为负时会反转数据 应用举例: 123456789101112131415>>> import numpy as np>>> a = np.array([[[1,2,3,4], [5,6,7,8]], [[9,10,11,12], [13,14,15,16]], [[17,18,19,20], [21,22,23,24]]])>>> print(a)[[[ 1 2 3 4] [ 5 6 7 8]] [[ 9 10 11 12] [13 14 15 16]] [[17 18 19 20] [21 22 23 24]]]>>> print(a.ravel())[ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24]>>> print(np.ravel(a))[ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24] 【6x03】numpy.resize()numpy.resize() 方法会直接修改所操作的数组,返回具有指定形状的新数组,如果新数组大于原始数组,则新数组将填充 a 的重复副本。 基本语法:numpy.resize(a, new_shape) 参数解释: 参数 描述 a 待转换的数组 new_shape 新数组的大小形状 12345678910111213141516>>> import numpy as np>>> a = np.array([[1, 2, 3], [4, 5, 6]])>>> print(a)[[1 2 3] [4 5 6]]>>> print(np.resize(a, (3, 2)))[[1 2] [3 4] [5 6]]>>> print(np.resize(a, (3, 3)))[[1 2 3] [4 5 6] [1 2 3]]>>> print(np.resize(a, (2, 4)))[[1 2 3 4] [5 6 1 2]] 【6x04】numpy.ndarray.flatten()numpy.ndarray.flatten() 方法恰如其名,flatten 就是展平的意思,与 ravel 函数的功能相同,二者的不同之处在于:flatten 方法会请求分配新的内存来保存结果,而 ravel 方法只是返回数组的一个视图(view)。 基本语法:ndarray.flatten(order='C') 其 order 参数的值可以是 C F A K,含义与 reshape 和 ravel 方法中参数的一样. 应用举例: 12345678910111213>>> import numpy as np>>> a = np.array([[[1,2,3,4], [5,6,7,8]], [[9,10,11,12], [13,14,15,16]], [[17,18,19,20], [21,22,23,24]]])>>> print(a)[[[ 1 2 3 4] [ 5 6 7 8]] [[ 9 10 11 12] [13 14 15 16]] [[17 18 19 20] [21 22 23 24]]]>>> print(a.flatten())[ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24] 【6x05】numpy.ndarray.shapenumpy.ndarray.shape 本来是 Ndarray 对象的一个属性,但可以通过直接用一个正整数元组对其赋值来设置数组的维度: 12345678910111213141516>>> import numpy as np>>> a = np.array([[[1,2,3,4], [5,6,7,8]], [[9,10,11,12], [13,14,15,16]], [[17,18,19,20], [21,22,23,24]]])>>> print(a)[[[ 1 2 3 4] [ 5 6 7 8]] [[ 9 10 11 12] [13 14 15 16]] [[17 18 19 20] [21 22 23 24]]]>>> a.shape = (3, 8)>>> print(a)[[ 1 2 3 4 5 6 7 8] [ 9 10 11 12 13 14 15 16] [17 18 19 20 21 22 23 24]] 【6x06】numpy.ndarray.transpose() & numpy.ndarray.Tndarray.transpose() 和 ndarray.T 方法的作用是对数组进行转置,即原来的行变成列,原来的列变成行。 1234567891011121314151617181920>>> import numpy as np>>> a = np.array([[0, 1, 2, 3, 4, 5], [6, 7, 8, 9, 10, 11], [12, 13, 14, 15, 16, 17]])>>> print(a)[[ 0 1 2 3 4 5] [ 6 7 8 9 10 11] [12 13 14 15 16 17]]>>> print(a.transpose())[[ 0 6 12] [ 1 7 13] [ 2 8 14] [ 3 9 15] [ 4 10 16] [ 5 11 17]]>>> print(a.T)[[ 0 6 12] [ 1 7 13] [ 2 8 14] [ 3 9 15] [ 4 10 16] [ 5 11 17]] 【6x07】numpy.swapaxes()numpy.swapaxes() 方法用于对换数组的两个轴 基本语法:numpy.swapaxes(a, axis1, axis2) 参数解释:a 为原始数组,axis1、axis2 分别对应两个轴,类型为整数 12345678910111213>>> import numpy as np>>> a = np.array([[0, 1, 2, 3, 4, 5], [6, 7, 8, 9, 10, 11], [12, 13, 14, 15, 16, 17]])>>> print(a)[[ 0 1 2 3 4 5] [ 6 7 8 9 10 11] [12 13 14 15 16 17]]>>> print(np.swapaxes(a, 1, 0)) # 交换 1 轴和 0 轴,此处相当于数组的转置,与【6x06】效果相同[[ 0 6 12] [ 1 7 13] [ 2 8 14] [ 3 9 15] [ 4 10 16] [ 5 11 17]] 【7x00】NumPy 数据类型NumPy 数组包含同一类型的值,支持的数据类型比 Python 内置的类型更多,构建一个数组时,可以用一个字符串参数 dtype 来指定数据类型: 1np.zeros(10, dtype='int16') 1np.zeros(10, dtype=np.int16) 数据类型 描述 bool_               布尔值(True 或者 False),用一个字节存储 int_               默认的整型(类似于 C 语言中的 long,通常情况下是 int32 或 int64) intc               同 C 语言的 int 相同(通常是 int32 或 int64) intp               用作索引的整型(和 C 语言的 ssize_t 相同,通常情况下是 int32 或 int64) int8               字节(byte,范围从 –128 到 127),可用 i1 缩写代替 int16               整型(范围从 –32768 到 32767),可用 i2 缩写代替 int32               整型(范围从 –2147483648 到 2147483647),可用 i4 缩写代替 int64               整型(范围从 –9223372036854775808 到 9223372036854775807),可用 i8 缩写代替 uint8               无符号整型(范围从 0 到 255) uint16               无符号整型(范围从 0 到 65535) uint32               无符号整型(范围从 0 到 4294967295) uint64               无符号整型(范围从 0 到 18446744073709551615) float_               float64 的简化形式 float16               半精度浮点型,包括:1 比特位符号,5 比特位指数,10 比特位尾数 float32               单精度浮点型,包括:1 比特位符号,8 比特位指数,23 比特位尾数 float64               双精度浮点型,包括:1 比特位符号,11 比特位指数,52 比特位尾数 complex_               complex128 的简化形式 complex64               复数,表示双 32 位浮点数(实数部分和虚数部分) complex128               复数,表示双 64 位浮点数(实数部分和虚数部分) 【8x00】NumPy 数组属性 属性 描述 ndarray.ndim 秩,即轴的数量或维度的数量,一维数组的秩为 1,二维数组的秩为 2,以此类推 ndarray.shape 数组的维度,对于矩阵,n 行 m 列 ndarray.size 数组元素的总个数,相当于 .shape 中 n*m 的值 ndarray.dtype ndarray 对象的元素类型 ndarray.itemsize ndarray 对象中每个元素的大小,以字节为单位 ndarray.flags ndarray 对象的内存信息 ndarray.real ndarray元素的实部 ndarray.imag ndarray 元素的虚部 ndarray.data 包含实际数组元素的缓冲区,由于一般通过数组的索引获取元素,所以通常不需要使用这个属性 其中 ndarray.flags 包含以下属性: 属性 描述 C_CONTIGUOUS (C) 数据是在一个单一的 C 风格的连续段中 F_CONTIGUOUS (F) 数据是在一个单一的 Fortran 风格的连续段中 OWNDATA (O) 数组拥有它所使用的内存或从另一个对象中借用它 WRITEABLE (W) 数据区域可以被写入,将该值设置为 False,则数据为只读 ALIGNED (A) 数据和所有元素都适当地对齐到硬件上 UPDATEIFCOPY (U) 这个数组是其它数组的一个副本,当这个数组被释放时,原数组的内容将被更新 应用举例: 12345678910>>> import numpy as np>>> a = np.array([1,2,3,4,5])>>> print(a.flags) C_CONTIGUOUS : True F_CONTIGUOUS : True OWNDATA : True WRITEABLE : True ALIGNED : True WRITEBACKIFCOPY : False UPDATEIFCOPY : False ndarray.shape 查看数组维度以及更改数组形状: 1234567891011121314>>> import numpy as np>>> a = np.array([[1,2,3],[4,5,6]])>>> print(a)[[1 2 3] [4 5 6]]>>> print(a.shape)(2, 3)>>> a.shape = (3, 2)>>> print(a)[[1 2] [3 4] [5 6]]>>> print(a.shape)(3, 2) 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/104870084未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"NumPy","slug":"Python-数据分析/NumPy","permalink":"https://www.itrhx.com/categories/Python-数据分析/NumPy/"}],"tags":[{"name":"NumPy","slug":"NumPy","permalink":"https://www.itrhx.com/tags/NumPy/"},{"name":"数组","slug":"数组","permalink":"https://www.itrhx.com/tags/数组/"}]},{"title":"用 VPS 搭建一个自己的 SSR 服务器","slug":"A61-build-a-SSR-server-with-VPS","date":"2020-01-10T13:38:13.786Z","updated":"2020-03-31T09:27:43.584Z","comments":true,"path":"2020/01/10/A61-build-a-SSR-server-with-VPS/","link":"","permalink":"https://www.itrhx.com/2020/01/10/A61-build-a-SSR-server-with-VPS/","excerpt":"俗话说得好:预先善其事,必先利其器,作为一个程序员,经常会用到 GitHub、Google、Stack Overflow 啥的,由于国内政策原因,想要访问国外网站就得科学上网,最常见的工具就是 ShadowsocksR,又被称为酸酸乳、SSR、小飞机,目前市面上有很多很多的机场,价格也不是很高,完全可以订阅别人的,但是订阅别人的,数据安全没有保障,有可能你的浏览历史啥的别人都能掌握,别人也有随时跑路的可能,总之,只有完全属于自己的东西才是最香的!","text":"俗话说得好:预先善其事,必先利其器,作为一个程序员,经常会用到 GitHub、Google、Stack Overflow 啥的,由于国内政策原因,想要访问国外网站就得科学上网,最常见的工具就是 ShadowsocksR,又被称为酸酸乳、SSR、小飞机,目前市面上有很多很多的机场,价格也不是很高,完全可以订阅别人的,但是订阅别人的,数据安全没有保障,有可能你的浏览历史啥的别人都能掌握,别人也有随时跑路的可能,总之,只有完全属于自己的东西才是最香的! 购买 VPSVPS(Virtual Private Server)即虚拟专用服务器技术,在购买 VPS 服务器的时候要选择国外的,推荐 Vultr,国际知名,性价比比较高,最低有$2.5/月、$3.5/月的,个人用的话应该足够了。 点击链接注册 Vultr 账号:https://www.vultr.com/?ref=8367048,目前新注册用户充值10刀可以赠送50刀,注册完毕之后来到充值页面,最低充值10刀,可以选择支付宝或者微信支付。 充值完毕之后,点击左侧 Products,选择服务器,一共有16个地区的,选择不同地区的服务器,最后的网速也有差别,那如何选择一个速度最优的呢?很简单,你可以一次性选择多个服务器,都部署上去,搭建完毕之后,测试其速度,选择最快的,最后再把其他的都删了,可能你会想,部署多个,那费用岂不是很贵,这里注意,虽然写的是多少钱一个月,而实际上它是按照小时计费的,从你部署之后开始计费,$5/月 ≈ $0.00694/小时,你部署完毕再删掉,这段时间的费用很低,可以忽略不计,一般来说,日本和新加坡的比较快一点,也有人说日本和新加坡服务器的端口封得比较多,容易搭建失败,具体可以自己测试一下,还有就是,只有部分地区的服务器有$2.5/月、$3.5/月的套餐,其中$2.5/月的只支持 IPv6,可以根据自己情况选择,最后操作系统建议选择 CentOS 7 x64 的,不然有可能搭建失败,后面还有个 Enable IPv6 的选项,对 IPv6 有需求的话可以勾上,其他选项就可以不用管了。 部署成功后,点 Server Details 可以看到服务器的详细信息,其中有 IP、用户名、密码等信息,后面搭建 SSR 的时候会用到,此时你可以 ping 一下你的服务器 IP,如果 ping 不通的话,可以删掉再重新开一个服务器。 搭建 SSR我们购买的是虚拟的服务器,因此需要工具远程连接到 VPS,如果是 Mac/Linux 系统,可以直接在终端用 SSH 连接 VPS: 1ssh root@你VPS的IP -p 22 (22是你VPS的SSH端口) 如果是 Windows 系统,可以用第三方工具连接到 VPS,如:Xshell、Putty 等,可以百度下载,以下以 Xshell 为例: 点击文件,新建会话,名称可以随便填,协议为 SSH,主机为你服务器的 IP 地址,点击确定,左侧双击这个会话开始连接,最开始会出现一个 SSH安全警告,点击接受并保存即可,然后会让你输入服务器的用户名和密码,直接在 Vultr 那边复制过来即可,最后看到 [root@vultr ~]# 字样表示连接成功。 连接成功后执行以下命令开始安装 SSR: 1wget --no-check-certificate https://freed.ga/github/shadowsocksR.sh; bash shadowsocksR.sh 如果提示 wget :command not found,可先执行 yum -y install wget,再执行上述命令即可。 执行完毕后会让你设置 SSR 连接密码和端口,然后按任意键开始搭建。 搭建成功后会显示你服务器 IP,端口,连接密码,协议等信息,这些信息要记住,后面使用 ShadowsocksR 的时候要用到。 安装锐速由于我们购买的服务器位于国外,如果遇到上网高峰期,速度就会变慢,而锐速就是一款专业的连接加速器,可以充分利用服务器带宽,提升带宽吞吐量,其他还有类似的程序如 Google BBR 等,可以自行比较其加速效果,以下以操作系统为 CentOS 6&7 锐速的安装为例。 如果你服务器操作系统选择的是 CentOS 6 x64,则直接执行以下命令,一直回车即可: 1wget --no-check-certificate -O appex.sh https://raw.githubusercontent.com/hombo125/doubi/master/appex.sh && bash appex.sh install '2.6.32-642.el6.x86_64' 如果你服务器操作系统选择的是 CentOS 7 x64,则需要先执行以下命令更换内核: 1wget --no-check-certificate -O rskernel.sh https://raw.githubusercontent.com/hombo125/doubi/master/rskernel.sh && bash rskernel.sh 如下图所示表示内核更换完毕,此时已经断开与服务器的连接,我们需要重新连接到服务器,再执行后面的操作: 重新连接到服务器后,再执行以下命令: 1yum install net-tools -y && wget --no-check-certificate -O appex.sh https://raw.githubusercontent.com/0oVicero0/serverSpeeder_Install/master/appex.sh && bash appex.sh install 然后一直回车即可,系统会自动安装锐速。 出现以下信息表示安装成功: 使用 SSR常见的工具有 ShadowsocksR、SSTap(原本是个游戏加速器,现在已经停止维护,但 GitHub 上仍然可以找到)等。 Shadowsocks 官网:https://shadowsocks.org/ShadowsocksR 下载地址:https://github.com/Anankke/SSRR-WindowsSSTap GitHub 地址:https://github.com/FQrabbit/SSTap-Rule 不管什么工具,用法都是一样的,添加一个新的代理服务器,服务器 IP、端口、密码、加密方式等等这些信息保持一致就行了。然后就可以愉快地科学上网了! 多端口配置经过以上步骤我们就可以科学上网了,但是目前为止只有一个端口,只能一个人用,那么如何实现多个端口多人使用呢?事实上端口、密码等信息是储存在一个叫做 shadowsocks.json 文件里的,如果要添加端口或者更改密码,只需要修改此文件即可。 连接到自己的 VPS,输入以下命令,使用 vim 编辑文件:vi /etc/shadowsocks.json 原文件内容大概如下: 1234567891011121314151617181920{ \"server\": \"0.0.0.0\", \"server_port\": 8686, \"server_ipv6\": \"::\", \"local_address\": \"127.0.0.1\", \"local_port\": 1081, \"password\":\"SSR12345\", \"timeout\": 120, \"udp_timeout\": 60, \"method\": \"aes-256-cfb\", \"protocol\": \"auth_sha1_v4_compatible\", \"protocol_param\": \"\", \"obfs\": \"http_simple_compatible\", \"obfs_param\": \"\", \"dns_ipv6\": false, \"connect_verbose_info\": 1, \"redirect\": \"\", \"fast_open\": false, \"workers\": 1} 增加端口,我们将其修改为如下内容: 1234567891011121314151617181920212223242526{ \"server\": \"0.0.0.0\", \"server_ipv6\": \"::\", \"local_address\": \"127.0.0.1\", \"local_port\": 1081, \"port_password\": { \"8686\":\"SSR1\", \"8687\":\"SSR2\", \"8688\":\"SSR3\", \"8689\":\"SSR4\", \"8690\":\"SSR5\" }, \"timeout\": 120, \"udp_timeout\": 60, \"method\": \"aes-256-cfb\", \"protocol\": \"auth_sha1_v4_compatible\", \"protocol_param\": \"\", \"obfs\": \"http_simple_compatible\", \"obfs_param\": \"\", \"dns_ipv6\": false, \"connect_verbose_info\": 1, \"redirect\": \"\", \"fast_open\": false, \"workers\": 1} 也就是删除原来的 server_port 和 password 这两项,然后增加 port_password 这一项,前面是端口号,后面是密码,注意不要把格式改错了!!!修改完毕并保存!!! 接下来配置一下防火墙,同样的,输入以下命令,用 vim 编辑文件:vi /etc/firewalld/zones/public.xml 初始的防火墙只开放了最初配置 SSR 默认的那个端口,现在需要我们手动加上那几个新加的端口,注意:一个端口需要复制两行,一行是 tcp,一行是 udp。 原文件内容大概如下: 12345678<?xml version=\"1.0\" encoding=\"utf-8\"?><zone> <short>Public</short> <service name=\"dhcpv6-client\"/> <service name=\"ssh\"/> <port protocol=\"tcp\" port=\"8686\"/> <port protocol=\"udp\" port=\"8686\"/></zone> 修改后的内容如下: 12345678910111213141516<?xml version=\"1.0\" encoding=\"utf-8\"?><zone> <short>Public</short> <service name=\"dhcpv6-client\"/> <service name=\"ssh\"/> <port protocol=\"tcp\" port=\"8686\"/> <port protocol=\"udp\" port=\"8686\"/> <port protocol=\"tcp\" port=\"8687\"/> <port protocol=\"udp\" port=\"8687\"/> <port protocol=\"tcp\" port=\"8688\"/> <port protocol=\"udp\" port=\"8688\"/> <port protocol=\"tcp\" port=\"8689\"/> <port protocol=\"udp\" port=\"8689\"/> <port protocol=\"tcp\" port=\"8690\"/> <port protocol=\"udp\" port=\"8690\"/></zone> 修改完毕并保存,最后重启一下 shadowsocks,然后重新载入防火墙即可,两条命令如下: 1/etc/init.d/shadowsocks restart 1firewall-cmd --reload 完成之后,我们新加的这几个端口就可以使用了 另外还可以将配置转换成我们常见的链接形式,如:ss://xxxxx 或 ssr://xxxxx,其实这种链接就是把 IP,端口,密码等信息按照一定的格式拼接起来,然后经过 Base64 编码后实现的,有兴趣或者有需求的可以自行百度。 扩展命令SSR 常用命令:启动:/etc/init.d/shadowsocks start停止:/etc/init.d/shadowsocks stop重启:/etc/init.d/shadowsocks restart状态:/etc/init.d/shadowsocks status卸载:./shadowsocks-all.sh uninstall更改配置参数:vim /etc/shadowsocks-r/config.json 我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=mpa02hyz07v5","categories":[{"name":"VPS","slug":"VPS","permalink":"https://www.itrhx.com/categories/VPS/"}],"tags":[{"name":"VPS","slug":"VPS","permalink":"https://www.itrhx.com/tags/VPS/"},{"name":"SSR","slug":"SSR","permalink":"https://www.itrhx.com/tags/SSR/"}]},{"title":"2019年总结【跨越今天,更不平凡】","slug":"A60-2019-summary","date":"2019-12-31T15:14:59.983Z","updated":"2020-08-06T02:59:52.727Z","comments":true,"path":"2019/12/31/A60-2019-summary/","link":"","permalink":"https://www.itrhx.com/2019/12/31/A60-2019-summary/","excerpt":"","text":"还记得小时候写作文,畅想2020会怎样怎样,光阴似箭,2020真的来了,度过了艰难的考试周,抽了个晚上,回想了一下,决定写一写总结吧,似乎以前都没写过呢,那干脆连带2017、2018也写写吧,重点写一写2019的,以后争取每年都做一下总结。 【2017】2017年高三,上半年就不用说了,所有高三考生都一个样吧,下半年考进了武汉的某二本院校,软件工程专业,现在回想起来,当时把时间浪费得太多了,最开始加了一个部门,后来退了(事实上啥也学不到,浪费时间 ),然后除了完成学校的课程以外,其他啥也没搞,剩下的时间基本上全拿来骑车了,从高一开始就热爱单车运动,刚上大学肯定得放飞自我了,没课的时候就天天和学长到处跑,都快把武汉跑了个遍了,当时还定了个计划,大学四年骑车去一次西藏或者青海湖,其他的什么都没想,也没有对以后具体干哪方面做过规划,这一年收获最多的应该就是路上的风景了。 【2018】2018上半年,大一下学期,学习方面就过了个英语四级,然后依旧热衷于我的单车,暑假的时候疯狂了一把,7天干了700多公里,从学校骑回家了,那个时候正是热的时候,白天基本上在三十度,从武汉往西边走,后面全是爬山,上山爬不动,下山刹不住,路上也遇到了不少牛逼人物,有徒步西藏的,有环游中国的,直播平台有好几十万粉丝的……遇到的人都很善良,很硬汉,这次经历从某种程度上来说也是一次成长吧,一次很有意义的骑行。 下半年,也就是大二开始,才慢慢开始重视专业知识的学习,大二上学期搭建了个人博客,开始尝试写博客,其实就是把博客当做笔记吧,记性不好,学了的东西容易忘记,忘记了可以经常翻自己博客再复习复习,自己踩过的坑也记录记录,后来没想到有些文章访问量还挺高的,在博客搭建方面也帮到了一些网友,最重要的是结识了不少博友,有各行各业的大佬,下半年也定了方向,开始专注Python的学习,从此开始慢慢熬夜,也渐渐地不怎么出去骑车了。 【2019】2019 总的来说,还比较满意吧,主要是感觉过得很充实,大三基本上每天一整天都是上机课,没有太多时间搞自己的,自己倾向于Python、网络爬虫、数据分析方面,然而这些课程学校都没有,每天晚上以及周六周日都是自己在学,找了不少视频在看,有时候感觉自己还是差点火候,感觉一个简单的东西人家看一遍就会,但是我要看好几遍,不管怎样,我还是相信勤能补拙的。 【学习方面】 [√] 通过软考中级软件设计师 [√] 成为入党积极分子 [√] 学校大课基金结题 英语六级未通过 国家专利未通过 【看完或者大部分看完的书籍】 [√] 《软件设计师考试》 [√] 《Python 编程从入门到实践》 [√] 《Python 编程从零基础到项目实战》 [√] 《Python3 网络爬虫开发实战》 [√] 《Python 网络爬虫从入门到实践》 [√] 《精通 Python 爬虫框架 Scrapy》 [√] 《Python 程序员面试宝典(算法+数据结构)》 [√] 《Selenium 自动化测试 — 基于 Python 语言》 [√] 《重构,改善既有代码的设计》 【生活方面】暑假受家族前辈的邀请,为整个姓氏家族编写族谱,感觉这是今年收获最大的一件事情吧,当时背着电脑跟着前辈下乡,挨家挨户统计资料,纯手工录入电脑(感觉那是我活了二十年打字打得最多的一个月,祖宗十八代都搞清楚了),最后排版打印成书,一个月下来感受到了信息化时代和传统文化的碰撞,见了很多古书,古迹,当然还领略到了古繁体字的魅力,前辈一路上给我讲述了很多书本上学不到的东西,一段很有意义的体验,感触颇深。 个人爱好上面,今年就基本上没有骑车了,没有经常骑车,开学骑了两次就跟不上别人了,后面就洗干净用布遮起来放在寝室了,按照目前情况来看,多半是要“退役”了,不知道何时才会又一次踩上脚踏,不过偶尔还是在抖音上刷刷关注的单车大佬,看看别人的视频,看到友链小伙伴 Shan San 在今年总结也写了他一年没有跳舞了,抛弃了曾经热爱的 Breaking,真的是深有感触啊。 有个遗憾就是大一的愿望实现不了了,恐怕大学四年也不会去西藏或者青海湖了,此处放一个到目前为止的骑行数据,以此纪念一下我的单车生涯吧。 【技术交流&实践】自从搭建了博客之后,认识了不少大佬,经常会去大佬博客逛逛,涨涨知识 截止目前,个人博客 PV:4万+,UV:1万+,知乎:400+赞同,CSDN:43万+访问量,400+赞同 此外今年第一次为开源做了一点儿微不足道的贡献,为 Hexo 博客主题 Material X 添加了文章字数统计和阅读时长的功能,提交了人生当中第一个 PR。第一次嘛,还是值得纪念一下的。 我 GitHub 上虽然有一些小绿点,但是很大一部分都是推送的博客相关的东西,剩下的有几个仓库也就是 Python 相关的了,一些实战的代码放在了上面,很多时候是拿 GitHub 围观一些牛逼代码或者资源,还需要努力学习啊! 实战方面,爬虫自己也爬了很多网站,遇到一些反爬网站还不能解决,也刷了一些 Checkio 上面的题,做了题,和其他大佬相比才会发现自己的代码水平有多低,最直接的感受就是我用了很多行代码,而大神一行代码就解决了,只能说自己的水平还有很大的增进空间,新的一年继续努力吧! 【2020】1024 + 996 = 2020,2020注定是不平凡的一年,定下目标,努力实现,只谈技术,莫问前程! 【计划目标】 4月蓝桥杯拿奖 5月通过软考高级信息系统项目管理师 6月通过英语六级 坚持记笔记、写博客 学习 JavaScript 逆向 研究网站常用反爬策略,掌握反反爬虫技术 掌握两到三个主流爬虫框架 加深 Python 算法和数据结构的学习 学习 Python 数据可视化和数据分析 做一个 Python 相关的优秀开源项目(爬虫类最好) 向优秀爬虫工程师方向迈进 参加 PyCon China 2020 【计划要看的书籍】 《JavaScript 从入门到精通》 《Python3 反爬虫原理与绕过实战》 《Python 数据可视化编程实战》 《Python 数据可视化之 matplotlib 实践》 《Python 数据可视化之 matplotlib 精进》 《基于 Python的大数据分析基础及实战》 123>>> pip uninstall 2019>>> pip install 2020>>> print('Live a good life, write some good code !!!')","categories":[{"name":"BLOG","slug":"BLOG","permalink":"https://www.itrhx.com/categories/BLOG/"}],"tags":[{"name":"年终总结","slug":"年终总结","permalink":"https://www.itrhx.com/tags/年终总结/"}]},{"title":"Python3 爬虫实战 — 瓜子全国二手车","slug":"A59-pyspider-guazi","date":"2019-11-14T16:10:55.649Z","updated":"2019-12-29T07:14:02.938Z","comments":true,"path":"2019/11/15/A59-pyspider-guazi/","link":"","permalink":"https://www.itrhx.com/2019/11/15/A59-pyspider-guazi/","excerpt":"爬取时间:2019-11-14爬取难度:★★☆☆☆☆请求链接:https://www.guazi.com/www/buy/爬取目标:爬取瓜子全国二手车信息,包括价格、上牌时间、表显里程等;保存车辆图片涉及知识:请求库 requests、解析库 lxml、Xpath 语法、数据库 MongoDB 的操作完整代码:https://github.com/TRHX/Python3-Spider-Practice/tree/master/guazi其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278","text":"爬取时间:2019-11-14爬取难度:★★☆☆☆☆请求链接:https://www.guazi.com/www/buy/爬取目标:爬取瓜子全国二手车信息,包括价格、上牌时间、表显里程等;保存车辆图片涉及知识:请求库 requests、解析库 lxml、Xpath 语法、数据库 MongoDB 的操作完整代码:https://github.com/TRHX/Python3-Spider-Practice/tree/master/guazi其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278 【1x00】提取所有二手车详情页URL分析页面,按照习惯,最开始在 headers 里面只加入 User-Agent 字段,向主页发送请求,然而返回的东西并不是主页真正的源码,因此我们加入 Cookie,再次发起请求,即可得到真实数据。 获取 Cookie:打开浏览器访问网站,打开开发工具,切换到 Network 选项卡,筛选 Doc 文件,在 Request Headers 里可以看到 Cookie 值。 注意在爬取瓜子二手车的时候,User-Agent 与 Cookie 要对应一致,也就是直接复制 Request Headers 里的 User-Agent 和 Cookie,不要自己定义一个 User-Agent,不然有可能获取不到信息! 分析页面,请求地址为:https://www.guazi.com/www/buy/ 第一页:https://www.guazi.com/www/buy/ 第二页:https://www.guazi.com/www/buy/o2c-1/ 第三页:https://www.guazi.com/www/buy/o3c-1/ 一共有50页数据,利用 for 循环,每次改变 URL 中 o2c-1 参数里面的数字即可实现所有页面的爬取,由于我们是想爬取每台二手车详情页的数据,所以定义一个 parse_index() 函数,提取每一页的所有详情页的 URL,保存在列表 url_list 中 1234567891011121314151617181920# 必须要有 Cookie 和 User-Agent,且两者必须对应(用浏览器访问网站后控制台里面复制)headers = { 'Cookie': 'uuid=06ce7520-ebd1-45bc-f41f-a95f2c9b2283; ganji_uuid=7044571161649671972745; lg=1; clueSourceCode=%2A%2300; user_city_id=-1; sessionid=fefbd4f8-0a06-4e8a-dc49-8856e1a02a07; Hm_lvt_936a6d5df3f3d309bda39e92da3dd52f=1573469368,1573541270,1573541964,1573715863; close_finance_popup=2019-11-14; cainfo=%7B%22ca_a%22%3A%22-%22%2C%22ca_b%22%3A%22-%22%2C%22ca_s%22%3A%22seo_baidu%22%2C%22ca_n%22%3A%22default%22%2C%22ca_medium%22%3A%22-%22%2C%22ca_term%22%3A%22-%22%2C%22ca_content%22%3A%22-%22%2C%22ca_campaign%22%3A%22-%22%2C%22ca_kw%22%3A%22-%22%2C%22ca_i%22%3A%22-%22%2C%22scode%22%3A%22-%22%2C%22keyword%22%3A%22-%22%2C%22ca_keywordid%22%3A%22-%22%2C%22display_finance_flag%22%3A%22-%22%2C%22platform%22%3A%221%22%2C%22version%22%3A1%2C%22client_ab%22%3A%22-%22%2C%22guid%22%3A%2206ce7520-ebd1-45bc-f41f-a95f2c9b2283%22%2C%22ca_city%22%3A%22wh%22%2C%22sessionid%22%3A%22fefbd4f8-0a06-4e8a-dc49-8856e1a02a07%22%7D; _gl_tracker=%7B%22ca_source%22%3A%22-%22%2C%22ca_name%22%3A%22-%22%2C%22ca_kw%22%3A%22-%22%2C%22ca_id%22%3A%22-%22%2C%22ca_s%22%3A%22self%22%2C%22ca_n%22%3A%22-%22%2C%22ca_i%22%3A%22-%22%2C%22sid%22%3A56473912809%7D; cityDomain=www; preTime=%7B%22last%22%3A1573720945%2C%22this%22%3A1573469364%2C%22pre%22%3A1573469364%7D; Hm_lpvt_936a6d5df3f3d309bda39e92da3dd52f=1573720946; rfnl=https://www.guazi.com/www/chevrolet/i2c-1r18/; antipas=675i0t513a7447M2L9y418Qq869', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36'}# 获取所有二手车详情页URLdef parse_index(): response = requests.get(url=url, headers=headers) tree = etree.HTML(response.text) url_list = tree.xpath('//li/a[@class=\"car-a\"]/@href') # print(len(url_list)) return url_listif __name__ == '__main__': for i in range(1, 51): url = 'https://www.guazi.com/www/buy/o%sc-1/' % i detail_urls = parse_index() 【2x00】获取二手车详细信息并保存图片前面的第一步我们已经获取到了二手车详情页的 URL,现在定义一个 parse_detail() 函数,向其中循环传入每一条 URL,利用 Xpath 语法匹配每一条信息,所有信息包含:标题、二手车价格、新车指导价、车主、上牌时间、表显里程、上牌地、排放标准、变速箱、排量、过户次数、看车地点、年检到期、交强险、商业险到期。 其中有部分信息可能包含空格,可以用 strip() 方法将其去掉。 需要注意的是,上牌地对应的是一个 class="three" 的 li 标签,有些二手车没有上牌地信息,匹配的结果将是空,在数据储存时就有可能出现数组越界的错误信息,所以这里可以加一个判断,如果没有上牌地信息,可以将其赋值为:未知。 保存车辆图片时,为了节省时间和空间,避免频繁爬取被封,所以只保存第一张图片,同样利用 Xpath 匹配到第一张图片的地址,以标题为图片的名称,定义储存路径后,以二进制形式保存图片。 最后整个函数返回的是一个列表 data,这个列表包含每辆二手车的所有信息 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113# 获取二手车详细信息def parse_detail(content): detail_response = requests.get(url=content, headers=headers) tree = etree.HTML(detail_response.text) # 标题 title = tree.xpath('//h2[@class=\"titlebox\"]/text()') # 移除字符串头尾空格 title = [t.strip() for t in title] # 匹配到两个元素,只取其中一个为标题 title = title[:1] # print(title) # 价格 price_old = tree.xpath('//span[@class=\"pricestype\"]/text()') # 移除字符串头尾空格 price_old = [p.strip() for p in price_old] # 加入单位 price_old = [''.join(price_old + ['万'])] # print(price_old) # 新车指导价 price_new = tree.xpath('//span[@class=\"newcarprice\"]/text()') # 移除字符串头尾空格 price_new = [p.strip() for p in price_new] # 对字符串进行切片,只取数字多少万 price_new = ['¥' + price_new[0].split('价')[1]] # print(price_new) # 车主 owner = tree.xpath('//dl/dt/span/text()') owner = [owner[0].replace('车主:', '')] # print(owner) # 上牌时间 spsj = tree.xpath('//li[@class=\"one\"]/div/text()') # print(spsj) # 表显里程 bxlc = tree.xpath('//li[@class=\"two\"]/div/text()') # print(bxlc) # 上牌地 spd = tree.xpath('//li[@class=\"three\"]/div/text()') # 某些二手车没有上牌地,没有的将其赋值为:未知 if len(spd) == 0: spd = ['未知'] # print(spd) # 排放标准 pfbz = tree.xpath('//li[@class=\"four\"]/div/text()') pfbz = pfbz[:1] # print(pfbz) # 变速箱 bsx = tree.xpath('//li[@class=\"five\"]/div/text()') # print(bsx) # 排量 pl = tree.xpath('//li[@class=\"six\"]/div/text()') # print(pl) # 过户次数 ghcs = tree.xpath('//li[@class=\"seven\"]/div/text()') ghcs = [g.strip() for g in ghcs] ghcs = ghcs[:1] # print(ghcs) # 看车地点 kcdd = tree.xpath('//li[@class=\"eight\"]/div/text()') # print(kcdd) # 年检到期 njdq = tree.xpath('//li[@class=\"nine\"]/div/text()') # print(njdq) # 交强险 jqx = tree.xpath('//li[@class=\"ten\"]/div/text()') # print(jqx) # 商业险到期 syxdq = tree.xpath('//li[@class=\"last\"]/div/text()') syxdq = [s.strip() for s in syxdq] syxdq = syxdq[:1] # print(syxdq) # 保存车辆图片 # 获取图片地址 pic_url = tree.xpath('//li[@class=\"js-bigpic\"]/img/@data-src')[0] pic_response = requests.get(pic_url) # 定义图片名称以及保存的文件夹 pic_name = title[0] + '.jpg' dir_name = 'guazi_pic' # 如果没有该文件夹则创建该文件夹 if not os.path.exists(dir_name): os.mkdir(dir_name) # 定义储存路径 pic_path = dir_name + '/' + pic_name with open(pic_path, \"wb\")as f: f.write(pic_response.content) # 将每辆二手车的所有信息合并为一个列表 data = title + price_old + price_new + owner + spsj + bxlc + spd + pfbz + bsx + pl + ghcs + kcdd + njdq + jqx + syxdq return dataif __name__ == '__main__': for i in range(1, 51): url = 'https://www.guazi.com/www/buy/o%sc-1/' % i detail_urls = parse_index() for detail_url in detail_urls: car_url = 'https://www.guazi.com' + detail_url car_data = parse_detail(car_url) 【3x00】将数据储存到 MongoDB定义数据储存函数 save_data() 使用 MongoClient() 方法,向其传入地址参数 host 和 端口参数 port,指定数据库为 guazi,集合为 esc 传入第二步 parse_detail() 函数返回的二手车信息的列表,依次读取其中的元素,每一个元素对应相应的信息名称 最后调用 insert_one() 方法,每次插入一辆二手车的数据 12345678910111213141516171819202122232425262728293031323334353637# 将数据储存到 MongoDBdef save_data(data): client = pymongo.MongoClient(host='localhost', port=27017) db = client.guazi collection = db.esc esc = { '标题': data[0], '二手车价格': data[1], '新车指导价': data[2], '车主': data[3], '上牌时间': data[4], '表显里程': data[5], '上牌地': data[6], '排放标准': data[7], '变速箱': data[8], '排量': data[9], '过户次数': data[10], '看车地点': data[11], '年检到期': data[12], '交强险': data[13], '商业险到期': data[14] } collection.insert_one(esc)if __name__ == '__main__': for i in range(1, 51): url = 'https://www.guazi.com/www/buy/o%sc-1/' % i detail_urls = parse_index() for detail_url in detail_urls: car_url = 'https://www.guazi.com' + detail_url car_data = parse_detail(car_url) save_data(car_data) # 在3-10秒之间随机暂停 time.sleep(random.randint(3, 10)) time.sleep(random.randint(5, 60)) print('所有数据爬取完毕!') 【4x00】完整代码123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182# =============================================# --*-- coding: utf-8 --*--# @Time : 2019-11-14# @Author : TRHX# @Blog : www.itrhx.com# @CSDN : https://blog.csdn.net/qq_36759224# @FileName: guazi.py# @Software: PyCharm# =============================================from lxml import etreeimport requestsimport pymongoimport timeimport randomimport os# 必须要有 Cookie 和 User-Agent,且两者必须对应(用浏览器访问网站后控制台里面复制)headers = { 'Cookie': 'uuid=06ce7520-ebd1-45bc-f41f-a95f2c9b2283; ganji_uuid=7044571161649671972745; lg=1; clueSourceCode=%2A%2300; user_city_id=-1; sessionid=fefbd4f8-0a06-4e8a-dc49-8856e1a02a07; Hm_lvt_936a6d5df3f3d309bda39e92da3dd52f=1573469368,1573541270,1573541964,1573715863; close_finance_popup=2019-11-14; cainfo=%7B%22ca_a%22%3A%22-%22%2C%22ca_b%22%3A%22-%22%2C%22ca_s%22%3A%22seo_baidu%22%2C%22ca_n%22%3A%22default%22%2C%22ca_medium%22%3A%22-%22%2C%22ca_term%22%3A%22-%22%2C%22ca_content%22%3A%22-%22%2C%22ca_campaign%22%3A%22-%22%2C%22ca_kw%22%3A%22-%22%2C%22ca_i%22%3A%22-%22%2C%22scode%22%3A%22-%22%2C%22keyword%22%3A%22-%22%2C%22ca_keywordid%22%3A%22-%22%2C%22display_finance_flag%22%3A%22-%22%2C%22platform%22%3A%221%22%2C%22version%22%3A1%2C%22client_ab%22%3A%22-%22%2C%22guid%22%3A%2206ce7520-ebd1-45bc-f41f-a95f2c9b2283%22%2C%22ca_city%22%3A%22wh%22%2C%22sessionid%22%3A%22fefbd4f8-0a06-4e8a-dc49-8856e1a02a07%22%7D; _gl_tracker=%7B%22ca_source%22%3A%22-%22%2C%22ca_name%22%3A%22-%22%2C%22ca_kw%22%3A%22-%22%2C%22ca_id%22%3A%22-%22%2C%22ca_s%22%3A%22self%22%2C%22ca_n%22%3A%22-%22%2C%22ca_i%22%3A%22-%22%2C%22sid%22%3A56473912809%7D; cityDomain=www; preTime=%7B%22last%22%3A1573720945%2C%22this%22%3A1573469364%2C%22pre%22%3A1573469364%7D; Hm_lpvt_936a6d5df3f3d309bda39e92da3dd52f=1573720946; rfnl=https://www.guazi.com/www/chevrolet/i2c-1r18/; antipas=675i0t513a7447M2L9y418Qq869', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36'}# 获取所有二手车详情页URLdef parse_index(): response = requests.get(url=url, headers=headers) tree = etree.HTML(response.text) url_list = tree.xpath('//li/a[@class=\"car-a\"]/@href') # print(len(url_list)) return url_list# 获取二手车详细信息def parse_detail(content): detail_response = requests.get(url=content, headers=headers) tree = etree.HTML(detail_response.text) # 标题 title = tree.xpath('//h2[@class=\"titlebox\"]/text()') # 移除字符串头尾空格 title = [t.strip() for t in title] # 匹配到两个元素,只取其中一个为标题 title = title[:1] # print(title) # 价格 price_old = tree.xpath('//span[@class=\"pricestype\"]/text()') # 移除字符串头尾空格 price_old = [p.strip() for p in price_old] # 加入单位 price_old = [''.join(price_old + ['万'])] # print(price_old) # 新车指导价 price_new = tree.xpath('//span[@class=\"newcarprice\"]/text()') # 移除字符串头尾空格 price_new = [p.strip() for p in price_new] # 对字符串进行切片,只取数字多少万 price_new = ['¥' + price_new[0].split('价')[1]] # print(price_new) # 车主 owner = tree.xpath('//dl/dt/span/text()') owner = [owner[0].replace('车主:', '')] # print(owner) # 上牌时间 spsj = tree.xpath('//li[@class=\"one\"]/div/text()') # print(spsj) # 表显里程 bxlc = tree.xpath('//li[@class=\"two\"]/div/text()') # print(bxlc) # 上牌地 spd = tree.xpath('//li[@class=\"three\"]/div/text()') # 某些二手车没有上牌地,没有的将其赋值为:未知 if len(spd) == 0: spd = ['未知'] # print(spd) # 排放标准 pfbz = tree.xpath('//li[@class=\"four\"]/div/text()') pfbz = pfbz[:1] # print(pfbz) # 变速箱 bsx = tree.xpath('//li[@class=\"five\"]/div/text()') # print(bsx) # 排量 pl = tree.xpath('//li[@class=\"six\"]/div/text()') # print(pl) # 过户次数 ghcs = tree.xpath('//li[@class=\"seven\"]/div/text()') ghcs = [g.strip() for g in ghcs] ghcs = ghcs[:1] # print(ghcs) # 看车地点 kcdd = tree.xpath('//li[@class=\"eight\"]/div/text()') # print(kcdd) # 年检到期 njdq = tree.xpath('//li[@class=\"nine\"]/div/text()') # print(njdq) # 交强险 jqx = tree.xpath('//li[@class=\"ten\"]/div/text()') # print(jqx) # 商业险到期 syxdq = tree.xpath('//li[@class=\"last\"]/div/text()') syxdq = [s.strip() for s in syxdq] syxdq = syxdq[:1] # print(syxdq) # 保存车辆图片 # 获取图片地址 pic_url = tree.xpath('//li[@class=\"js-bigpic\"]/img/@data-src')[0] pic_response = requests.get(pic_url) # 定义图片名称以及保存的文件夹 pic_name = title[0] + '.jpg' dir_name = 'guazi_pic' # 如果没有该文件夹则创建该文件夹 if not os.path.exists(dir_name): os.mkdir(dir_name) # 定义储存路径 pic_path = dir_name + '/' + pic_name with open(pic_path, \"wb\")as f: f.write(pic_response.content) # 将每辆二手车的所有信息合并为一个列表 data = title + price_old + price_new + owner + spsj + bxlc + spd + pfbz + bsx + pl + ghcs + kcdd + njdq + jqx + syxdq return data# 将数据储存到 MongoDBdef save_data(data): client = pymongo.MongoClient(host='localhost', port=27017) db = client.guazi collection = db.esc esc = { '标题': data[0], '二手车价格': data[1], '新车指导价': data[2], '车主': data[3], '上牌时间': data[4], '表显里程': data[5], '上牌地': data[6], '排放标准': data[7], '变速箱': data[8], '排量': data[9], '过户次数': data[10], '看车地点': data[11], '年检到期': data[12], '交强险': data[13], '商业险到期': data[14] } collection.insert_one(esc)if __name__ == '__main__': for i in range(1, 51): num = 0 print('正在爬取第' + str(i) + '页数据...') url = 'https://www.guazi.com/www/buy/o%sc-1/' % i detail_urls = parse_index() for detail_url in detail_urls: car_url = 'https://www.guazi.com' + detail_url car_data = parse_detail(car_url) save_data(car_data) num += 1 print('第' + str(num) + '条数据爬取完毕!') # 在3-10秒之间随机暂停 time.sleep(random.randint(3, 10)) print('第' + str(i) + '页数据爬取完毕!') print('=====================') time.sleep(random.randint(5, 60)) print('所有数据爬取完毕!') 【5x00】数据截图爬取的汽车图片: 储存到 MongoDB 的数据: 数据导出为 CSV 文件: 【6x00】程序不足的地方Cookie 过一段时间就会失效,数据还没爬取完就失效了,导致无法继续爬取;爬取效率不高,可以考虑多线程爬取","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫实战","slug":"Python3-学习笔记/爬虫实战","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫实战/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"瓜子二手车","slug":"瓜子二手车","permalink":"https://www.itrhx.com/tags/瓜子二手车/"}]},{"title":"Python3 爬虫实战 — 58同城武汉出租房【加密字体对抗】","slug":"A58-pyspider-58tongcheng","date":"2019-10-21T13:22:53.980Z","updated":"2019-10-21T13:32:53.413Z","comments":true,"path":"2019/10/21/A58-pyspider-58tongcheng/","link":"","permalink":"https://www.itrhx.com/2019/10/21/A58-pyspider-58tongcheng/","excerpt":"爬取时间:2019-10-21爬取难度:★★★☆☆☆请求链接:https://wh.58.com/chuzu/爬取目标:58同城武汉出租房的所有信息涉及知识:网站加密字体的攻克、请求库 requests、解析库 Beautiful Soup、数据库 MySQL 的操作完整代码:https://github.com/TRHX/Python3-Spider-Practice/tree/master/58tongcheng其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278","text":"爬取时间:2019-10-21爬取难度:★★★☆☆☆请求链接:https://wh.58.com/chuzu/爬取目标:58同城武汉出租房的所有信息涉及知识:网站加密字体的攻克、请求库 requests、解析库 Beautiful Soup、数据库 MySQL 的操作完整代码:https://github.com/TRHX/Python3-Spider-Practice/tree/master/58tongcheng其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278 【1x00】加密字体攻克思路F12 打开调试模板,通过页面分析,可以观察到,网站里面凡是涉及到有数字的地方,都是显示为乱码,这种情况就是字体加密了,那么是通过什么手段实现字体加密的呢? CSS 中有一个 @font-face 规则,它允许为网页指定在线字体,也就是说可以引入自定义字体,这个规则本意是用来消除对电脑字体的依赖,现在不少网站也利用这个规则来实现反爬 右侧可以看到网站用的字体,其他的都是常见的微软雅黑,宋体等,但是有一个特殊的:fangchan-secret ,不难看出这应该就是58同城的自定义字体了 我们通过控制台看到的乱码事实上是由于 unicode 编码导致,查看网页源代码,我们才能看到他真正的编码信息 要攻克加密字体,那么我们肯定要分析他的字体文件了,先想办法得到他的加密字体文件,同样查看源代码,在源代码中搜索 fangchan-secret 的字体信息 选中的蓝色部分就是 base64 编码的加密字体字符串了,我们将其解码成二进制编码,写进 .woff 的字体文件,这个过程可以通过以下代码实现: 1234567891011121314151617import requestsimport base64headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'}url = 'https://wh.58.com/chuzu/'response = requests.get(url=url, headers=headers)# 匹配 base64 编码的加密字体字符串base64_string = response.text.split(\"base64,\")[1].split(\"'\")[0].strip()# 将 base64 编码的字体字符串解码成二进制编码bin_data = base64.decodebytes(base64_string.encode())# 保存为字体文件with open('58font.woff', 'wb') as f: f.write(bin_data) 得到字体文件后,我们可以通过 FontCreator 这个软件来看看字体对应的编码是什么: 观察我们在网页源代码中看到的编码:类似于 &#x9fa4;、&#x9f92; 对比字体文件对应的编码:类似于 uni9FA4、nui9F92 可以看到除了前面三个字符不一样以外,后面的字符都是一样的,只不过英文大小写有所差异 现在我们可能会想到,直接把编码替换成对应的数字不就OK了?然而并没有这么简单 尝试刷新一下网页,可以观察到 base64 编码的加密字体字符串会改变,也就是说编码和数字并不是一一对应的,再次获取几个字体文件,通过对比就可以看出来 可以看到,虽然每次数字对应的编码都不一样,但是编码总是这10个,是不变的,那么编码与数字之间肯定存在某种对应关系,,我们可以将字体文件转换为 xml 文件来观察其中的对应关系,改进原来的代码即可实现转换功能: 123456789101112131415161718192021import requestsimport base64from fontTools.ttLib import TTFontheaders = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'}url = 'https://wh.58.com/chuzu/'response = requests.get(url=url, headers=headers)# 匹配 base64 编码的加密字体字符串base64_string = response.text.split(\"base64,\")[1].split(\"'\")[0].strip()# 将 base64 编码的字体字符串解码成二进制编码bin_data = base64.decodebytes(base64_string.encode())# 保存为字体文件with open('58font.woff', 'wb') as f: f.write(bin_data)# 获取字体文件,将其转换为xml文件font = TTFont('58font.woff')font.saveXML('58font.xml') 打开 58font.xml 文件并分析,在 <cmap> 标签内可以看到熟悉的类似于 0x9476、0x958f 的编码,其后四位字符恰好是网页字体的加密编码,可以看到每一个编码后面都对应了一个 glyph 开头的编码 将其与 58font.woff 文件对比,可以看到 code 为 0x958f 这个编码对应的是数字 3,对应的 name 编码是 glyph00004 我们再次获取一个字体文件作为对比分析 依然是 0x958f 这个编码,两次对应的 name 分别是 glyph00004 和 glyph00007,两次对应的数字分别是 3 和 6,那么结论就来了,每次发送请求,code 对应的 name 会随机发生变化,而 name 对应的数字不会发生变化,glyph00001 对应数字 0、glyph00002 对应数字 1,以此类推 那么以 glyph 开头的编码是如何对应相应的数字的呢?在 xml 文件里面,每个编码都有一个 TTGlyph 的标签,标签里面是一行一行的类似于 x,y 坐标的东西,这个其实就是用来绘制字体的,用 matplotlib 根据坐标画个图,就可以看到是一个数字 此时,我们就知道了编码与数字的对应关系,下一步,我们可以查找 xml 文件里,编码对应的 name 的值,也就是以 glyph 开头的编码,然后返回其对应的数字,再替换掉网页源代码里的编码,就能成功获取到我们需要的信息了! 总结一下攻克加密字体的大致思路: 分析网页,找到对应的加密字体文件 如果引用的加密字体是一个 base64 编码的字符串,则需要转换成二进制并保存到 woff 字体文件中 将字体文件转换成 xml 文件 用 FontCreator 软件观察字体文件,结合 xml 文件,分析其编码与真实字体的关系 搞清楚编码与字体的关系后,想办法将编码替换成正常字体 【2x00】思维导图 【3x00】加密字体处理模块【3x01】获取字体文件并转换为xml文件12345678910111213141516def get_font(page_url, page_num): response = requests.get(url=page_url, headers=headers) # 匹配 base64 编码的加密字体字符串 base64_string = response.text.split(\"base64,\")[1].split(\"'\")[0].strip() # print(base64_string) # 将 base64 编码的字体字符串解码成二进制编码 bin_data = base64.decodebytes(base64_string.encode()) # 保存为字体文件 with open('58font.woff', 'wb') as f: f.write(bin_data) print('第' + str(page_num) + '次访问网页,字体文件保存成功!') # 获取字体文件,将其转换为xml文件 font = TTFont('58font.woff') font.saveXML('58font.xml') print('已成功将字体文件转换为xml文件!') return response.text 由主函数传入要发送请求的 url,利用字符串的 split() 方法,匹配 base64 编码的加密字体字符串,利用 base64 模块的 base64.decodebytes() 方法,将 base64 编码的字体字符串解码成二进制编码并保存为字体文件,利用 FontTools 库,将字体文件转换为 xml 文件 【3x02】将加密字体编码与真实字体进行匹配12345678910111213141516171819202122232425262728293031def find_font(): # 以glyph开头的编码对应的数字 glyph_list = { 'glyph00001': '0', 'glyph00002': '1', 'glyph00003': '2', 'glyph00004': '3', 'glyph00005': '4', 'glyph00006': '5', 'glyph00007': '6', 'glyph00008': '7', 'glyph00009': '8', 'glyph00010': '9' } # 十个加密字体编码 unicode_list = ['0x9476', '0x958f', '0x993c', '0x9a4b', '0x9e3a', '0x9ea3', '0x9f64', '0x9f92', '0x9fa4', '0x9fa5'] num_list = [] # 利用xpath语法匹配xml文件内容 font_data = etree.parse('./58font.xml') for unicode in unicode_list: # 依次循环查找xml文件里code对应的name result = font_data.xpath(\"//cmap//map[@code='{}']/@name\".format(unicode))[0] # print(result) # 循环字典的key,如果code对应的name与字典的key相同,则得到key对应的value for key in glyph_list.keys(): if key == result: num_list.append(glyph_list[key]) print('已成功找到编码所对应的数字!') # print(num_list) # 返回value列表 return num_list 由前面的分析,我们知道 name 的值(即以 glyph 开头的编码)对应的数字是固定的,glyph00001 对应数字 0、glyph00002 对应数字 1,以此类推,所以可以将其构造成为一个字典 glyph_list 同样将十个 code(即类似于 0x9476 的加密字体编码)构造成一个列表 循环查找这十个 code 在 xml 文件里对应的 name 的值,然后将 name 的值与字典文件的 key 值进行对比,如果两者值相同,则获取这个 key 的 value 值,最终得到的列表 num_list,里面的元素就是 unicode_list 列表里面每个加密字体的真实值 【3x03】替换掉网页中所有的加密字体编码12345def replace_font(num, page_response): # 9476 958F 993C 9A4B 9E3A 9EA3 9F64 9F92 9FA4 9FA5 result = page_response.replace('&#x9476;', num[0]).replace('&#x958f;', num[1]).replace('&#x993c;', num[2]).replace('&#x9a4b;', num[3]).replace('&#x9e3a;', num[4]).replace('&#x9ea3;', num[5]).replace('&#x9f64;', num[6]).replace('&#x9f92;', num[7]).replace('&#x9fa4;', num[8]).replace('&#x9fa5;', num[9]) print('已成功将所有加密字体替换!') return result 传入由上一步 find_font() 函数得到的真实字体的列表,利用 replace() 方法,依次将十个加密字体编码替换掉 【4x00】租房信息提取模块1234567891011121314151617181920212223242526272829303132333435363738def parse_pages(pages): num = 0 soup = BeautifulSoup(pages, 'lxml') # 查找到包含所有租房的li标签 all_house = soup.find_all('li', class_='house-cell') for house in all_house: # 标题 title = house.find('a', class_='strongbox').text.strip() # print(title) # 价格 price = house.find('div', class_='money').text.strip() # print(price) # 户型和面积 layout = house.find('p', class_='room').text.replace(' ', '') # print(layout) # 楼盘和地址 address = house.find('p', class_='infor').text.replace(' ', '').replace('\\n', '') # print(address) # 如果存在经纪人 if house.find('div', class_='jjr'): agent = house.find('div', class_='jjr').text.replace(' ', '').replace('\\n', '') # 如果存在品牌公寓 elif house.find('p', class_='gongyu'): agent = house.find('p', class_='gongyu').text.replace(' ', '').replace('\\n', '') # 如果存在个人房源 else: agent = house.find('p', class_='geren').text.replace(' ', '').replace('\\n', '') # print(agent) data = [title, price, layout, address, agent] save_to_mysql(data) num += 1 print('第' + str(num) + '条数据爬取完毕,暂停3秒!') time.sleep(3) 利用 BeautifulSoup 解析库很容易提取到相关信息,这里要注意的是,租房信息来源分为三种:经纪人、品牌公寓和个人房源,这三个的元素节点也不一样,因此匹配的时候要注意 【5x00】MySQL数据储存模块【5x01】创建MySQL数据库的表123456def create_mysql_table(): db = pymysql.connect(host='localhost', user='root', password='000000', port=3306, db='58tc_spiders') cursor = db.cursor() sql = 'CREATE TABLE IF NOT EXISTS 58tc_data (title VARCHAR(255) NOT NULL, price VARCHAR(255) NOT NULL, layout VARCHAR(255) NOT NULL, address VARCHAR(255) NOT NULL, agent VARCHAR(255) NOT NULL)' cursor.execute(sql) db.close() 首先指定数据库为 58tc_spiders,需要事先使用 MySQL 语句创建,也可以通过 MySQL Workbench 手动创建 然后使用 SQL 语句创建 一个表:58tc_data,表中包含 title、price、layout、address、agent 五个字段,类型都为 varchar 此创建表的操作也可以事先手动创建,手动创建后就不需要此函数了 【5x02】将数据储存到MySQL数据库12345678910def save_to_mysql(data): db = pymysql.connect(host='localhost', user='root', password='000000', port=3306, db='58tc_spiders') cursor = db.cursor() sql = 'INSERT INTO 58tc_data(title, price, layout, address, agent) values(%s, %s, %s, %s, %s)' try: cursor.execute(sql, (data[0], data[1], data[2], data[3], data[4])) db.commit() except: db.rollback() db.close() commit() 方法的作用是实现数据插入,是真正将语句提交到数据库执行的方法,使用 try except 语句实现异常处理,如果执行失败,则调用 rollback() 方法执行数据回滚,保证原数据不被破坏 【6x00】完整代码123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160# =============================================# --*-- coding: utf-8 --*--# @Time : 2019-10-21# @Author : TRHX# @Blog : www.itrhx.com# @CSDN : https://blog.csdn.net/qq_36759224# @FileName: 58tongcheng.py# @Software: PyCharm# =============================================import requestsimport timeimport randomimport base64import pymysqlfrom lxml import etreefrom bs4 import BeautifulSoupfrom fontTools.ttLib import TTFontheaders = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'}# 获取字体文件并转换为xml文件def get_font(page_url, page_num): response = requests.get(url=page_url, headers=headers) # 匹配 base64 编码的加密字体字符串 base64_string = response.text.split(\"base64,\")[1].split(\"'\")[0].strip() # print(base64_string) # 将 base64 编码的字体字符串解码成二进制编码 bin_data = base64.decodebytes(base64_string.encode()) # 保存为字体文件 with open('58font.woff', 'wb') as f: f.write(bin_data) print('第' + str(page_num) + '次访问网页,字体文件保存成功!') # 获取字体文件,将其转换为xml文件 font = TTFont('58font.woff') font.saveXML('58font.xml') print('已成功将字体文件转换为xml文件!') return response.text# 将加密字体编码与真实字体进行匹配def find_font(): # 以glyph开头的编码对应的数字 glyph_list = { 'glyph00001': '0', 'glyph00002': '1', 'glyph00003': '2', 'glyph00004': '3', 'glyph00005': '4', 'glyph00006': '5', 'glyph00007': '6', 'glyph00008': '7', 'glyph00009': '8', 'glyph00010': '9' } # 十个加密字体编码 unicode_list = ['0x9476', '0x958f', '0x993c', '0x9a4b', '0x9e3a', '0x9ea3', '0x9f64', '0x9f92', '0x9fa4', '0x9fa5'] num_list = [] # 利用xpath语法匹配xml文件内容 font_data = etree.parse('./58font.xml') for unicode in unicode_list: # 依次循环查找xml文件里code对应的name result = font_data.xpath(\"//cmap//map[@code='{}']/@name\".format(unicode))[0] # print(result) # 循环字典的key,如果code对应的name与字典的key相同,则得到key对应的value for key in glyph_list.keys(): if key == result: num_list.append(glyph_list[key]) print('已成功找到编码所对应的数字!') # print(num_list) # 返回value列表 return num_list# 替换掉网页中所有的加密字体编码def replace_font(num, page_response): # 9476 958F 993C 9A4B 9E3A 9EA3 9F64 9F92 9FA4 9FA5 result = page_response.replace('&#x9476;', num[0]).replace('&#x958f;', num[1]).replace('&#x993c;', num[2]).replace('&#x9a4b;', num[3]).replace('&#x9e3a;', num[4]).replace('&#x9ea3;', num[5]).replace('&#x9f64;', num[6]).replace('&#x9f92;', num[7]).replace('&#x9fa4;', num[8]).replace('&#x9fa5;', num[9]) print('已成功将所有加密字体替换!') return result# 提取租房信息def parse_pages(pages): num = 0 soup = BeautifulSoup(pages, 'lxml') # 查找到包含所有租房的li标签 all_house = soup.find_all('li', class_='house-cell') for house in all_house: # 标题 title = house.find('a', class_='strongbox').text.strip() # print(title) # 价格 price = house.find('div', class_='money').text.strip() # print(price) # 户型和面积 layout = house.find('p', class_='room').text.replace(' ', '') # print(layout) # 楼盘和地址 address = house.find('p', class_='infor').text.replace(' ', '').replace('\\n', '') # print(address) # 如果存在经纪人 if house.find('div', class_='jjr'): agent = house.find('div', class_='jjr').text.replace(' ', '').replace('\\n', '') # 如果存在品牌公寓 elif house.find('p', class_='gongyu'): agent = house.find('p', class_='gongyu').text.replace(' ', '').replace('\\n', '') # 如果存在个人房源 else: agent = house.find('p', class_='geren').text.replace(' ', '').replace('\\n', '') # print(agent) data = [title, price, layout, address, agent] save_to_mysql(data) num += 1 print('第' + str(num) + '条数据爬取完毕,暂停3秒!') time.sleep(3)# 创建MySQL数据库的表:58tc_datadef create_mysql_table(): db = pymysql.connect(host='localhost', user='root', password='000000', port=3306, db='58tc_spiders') cursor = db.cursor() sql = 'CREATE TABLE IF NOT EXISTS 58tc_data (title VARCHAR(255) NOT NULL, price VARCHAR(255) NOT NULL, layout VARCHAR(255) NOT NULL, address VARCHAR(255) NOT NULL, agent VARCHAR(255) NOT NULL)' cursor.execute(sql) db.close()# 将数据储存到MySQL数据库def save_to_mysql(data): db = pymysql.connect(host='localhost', user='root', password='000000', port=3306, db='58tc_spiders') cursor = db.cursor() sql = 'INSERT INTO 58tc_data(title, price, layout, address, agent) values(%s, %s, %s, %s, %s)' try: cursor.execute(sql, (data[0], data[1], data[2], data[3], data[4])) db.commit() except: db.rollback() db.close()if __name__ == '__main__': create_mysql_table() print('MySQL表58tc_data创建成功!') for i in range(1, 71): url = 'https://wh.58.com/chuzu/pn' + str(i) + '/' response = get_font(url, i) num_list = find_font() pro_pages = replace_font(num_list, response) parse_pages(pro_pages) print('第' + str(i) + '页数据爬取完毕!') time.sleep(random.randint(3, 60)) print('所有数据爬取完毕!') 【7x00】数据截图","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫实战","slug":"Python3-学习笔记/爬虫实战","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫实战/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"58同城","slug":"58同城","permalink":"https://www.itrhx.com/tags/58同城/"}]},{"title":"Python3 爬虫实战 — 模拟登陆12306【点触验证码对抗】","slug":"A57-pyspider-12306-login","date":"2019-10-21T08:41:50.349Z","updated":"2019-10-21T13:33:47.617Z","comments":true,"path":"2019/10/21/A57-pyspider-12306-login/","link":"","permalink":"https://www.itrhx.com/2019/10/21/A57-pyspider-12306-login/","excerpt":"登陆时间:2019-10-21实现难度:★★★☆☆☆请求链接:https://kyfw.12306.cn/otn/resources/login.html实现目标:模拟登陆中国铁路12306,攻克点触验证码涉及知识:点触验证码的攻克、自动化测试工具 Selenium 的使用、对接在线打码平台完整代码:https://github.com/TRHX/Python3-Spider-Practice/tree/master/12306-login其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278","text":"登陆时间:2019-10-21实现难度:★★★☆☆☆请求链接:https://kyfw.12306.cn/otn/resources/login.html实现目标:模拟登陆中国铁路12306,攻克点触验证码涉及知识:点触验证码的攻克、自动化测试工具 Selenium 的使用、对接在线打码平台完整代码:https://github.com/TRHX/Python3-Spider-Practice/tree/master/12306-login其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278 【1x00】思维导图 利用自动化测试工具 Selenium 直接模拟人的行为方式来完成验证 发送请求,出现验证码后,剪裁并保存验证码图片 选择在线打码平台,获取其API,以字节流格式发送图片 打码平台人工识别验证码,返回验证码的坐标信息 解析返回的坐标信息,模拟点击验证码,完成验证后点击登陆 【2x00】打码平台选择关于打码平台:在线打码平台全部都是人工在线识别,准确率非常高,原理就是先将验证码图片提交给平台,平台会返回识别结果在图片中的坐标位置,然后我们再解析坐标模拟点击即可,常见的打码平台有超级鹰、云打码等,打码平台是收费的,拿超级鹰来说,1元 = 1000题分,识别一次验证码将花费一定的题分,不同类型验证码需要的题分不同,验证码越复杂所需题分越高,比如 7 位中文汉字需要 70 题分,常见 4 ~ 6 位英文数字只要 10 题分,其他打码平台价格也都差不多,本次实战使用超级鹰打码平台 使用打码平台:在超级鹰打码平台注册账号,官网:http://www.chaojiying.com/ ,充值一块钱得到 1000 题分,在用户中心里面申请一个软件 ID ,在价格体系里面确定验证码的类型,先观察 12306 官网,发现验证码是要我们点击所有满足条件的图片,一般有 1 至 4 张图片满足要求,由此可确定在超级鹰打码平台的验证码类型为 9004(坐标多选,返回1~4个坐标,如:x1,y1|x2,y2|x3,y3), 然后在开发文档里面获取其 Python API,下载下来以备后用 【3x00】初始化模块【3x01】初始化函数1234567891011121314151617181920212223242526# 12306账号密码USERNAME = '155********'PASSWORD = '***********'# 超级鹰打码平台账号密码CHAOJIYING_USERNAME = '*******'CHAOJIYING_PASSWORD = '*******'# 超级鹰打码平台软件IDCHAOJIYING_SOFT_ID = '********'# 验证码类型CHAOJIYING_KIND = '9004'class CrackTouClick(): def __init__(self): self.url = 'https://kyfw.12306.cn/otn/resources/login.html' # path是谷歌浏览器驱动的目录,如果已经将目录添加到系统变量,则不用设置此路径 path = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe' chrome_options = Options() chrome_options.add_argument('--start-maximized') self.browser = webdriver.Chrome(executable_path=path, chrome_options=chrome_options) self.wait = WebDriverWait(self.browser, 20) self.username = USERNAME self.password = PASSWORD self.chaojiying = ChaojiyingClient(CHAOJIYING_USERNAME, CHAOJIYING_PASSWORD, CHAOJIYING_SOFT_ID) 定义 12306 账号(USERNAME)、密码(PASSWORD)、超级鹰用户名(CHAOJIYING_USERNAME)、超级鹰登录密码(CHAOJIYING_PASSWORD)、超级鹰软件 ID(CHAOJIYING_SOFT_ID)、验证码类型(CHAOJIYING_KIND),登录页面 url ,谷歌浏览器驱动的目录(path),浏览器启动参数等,将超级鹰账号密码等相关参数传递给超级鹰 API 【3x02】账号密码输入函数12345678910111213def get_input_element(self): # 登录页面发送请求 self.browser.get(self.url) # 登录页面默认是扫码登录,所以首先要点击账号登录 login = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.login-hd-account'))) login.click() time.sleep(3) # 查找到账号密码输入位置的元素 username = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'input#J-userName'))) password = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'input#J-password'))) # 输入账号密码 username.send_keys(self.username) password.send_keys(self.password) 分析页面可知,登陆页面默认出现的是扫描二维码登陆,所以要先点击账号登录,找到该 CSS 元素为 login-hd-account,调用 click() 方法实现模拟点击,此时出现账号密码输入框,同样找到其 ID 分别为 J-userName 和 J-password,调用 send_keys() 方法输入账号密码 【4x00】验证码处理模块1234567891011121314151617181920212223242526def crack(self): # 调用账号密码输入函数 self.get_input_element() # 调用验证码图片剪裁函数 image = self.get_touclick_image() bytes_array = BytesIO() image.save(bytes_array, format='PNG') # 利用超级鹰打码平台的 API PostPic() 方法把图片发送给超级鹰后台,发送的图像是字节流格式,返回的结果是一个JSON result = self.chaojiying.PostPic(bytes_array.getvalue(), CHAOJIYING_KIND) print(result) # 调用验证码坐标解析函数 locations = self.get_points(result) # 调用模拟点击验证码函数 self.touch_click_words(locations) # 调用模拟点击登录函数 self.login() try: # 查找是否出现用户的姓名,若出现表示登录成功 success = self.wait.until(EC.text_to_be_present_in_element((By.CSS_SELECTOR, '.welcome-name'), '谭先生')) print(success) cc = self.browser.find_element(By.CSS_SELECTOR, '.welcome-name') print('用户' + cc.text + '登录成功') # 若没有出现表示登录失败,继续重试,超级鹰会返回本次识别的分值 except TimeoutException: self.chaojiying.ReportError(result['pic_id']) self.crack() crack() 为验证码处理模块的主函数 调用账号密码输入函数 get_input_element(),等待账号密码输入完毕 调用验证码图片剪裁函数 get_touclick_image(),得到验证码图片 利用超级鹰打码平台的 API PostPic() 方法把图片发送给超级鹰后台,发送的图像是字节流格式,返回的结果是一个JSON,如果识别成功,典型的返回结果类似于: 12{'err_no': 0, 'err_str': 'OK', 'pic_id': '6002001380949200001', 'pic_str': '132,127|56,77', 'md5': '1f8e1d4bef8b11484cb1f1f34299865b'} 其中,pic_str 就是识别的文字的坐标,是以字符串形式返回的,每个坐标都以 | 分隔 调用 get_points() 函数解析超级鹰识别结果 调用 touch_click_words() 函数对符合要求的图片进行点击 调用模拟点击登录函数 login(),点击登陆按钮模拟登陆 使用 try-except 语句判断是否出现了用户信息,判断依据是是否有用户姓名的出现,出现的姓名和实际姓名一致则登录成功,如果失败了就重试,超级鹰会返回该分值 【4x01】验证码图片剪裁函数1234567891011121314def get_touclick_image(self, name='12306.png'): # 获取验证码的位置 element = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.login-pwd-code'))) time.sleep(3) location = element.location size = element.size top, bottom, left, right = location['y'], location['y'] + size['height'], location['x'], location['x'] + size['width'] # 先对整个页面截图 screenshot = self.browser.get_screenshot_as_png() screenshot = Image.open(BytesIO(screenshot)) # 根据验证码坐标信息,剪裁出验证码图片 captcha = screenshot.crop((left, top, right, bottom)) captcha.save(name) return captcha 首先查找到验证码的坐标信息,先对整个页面截图,然后根据验证码坐标信息,剪裁出验证码图片 location 属性可以返回该图片对象在浏览器中的位置,坐标轴是以屏幕左上角为原点,x 轴向右递增,y 轴向下递增,size 属性可以返回该图片对象的高度和宽度,由此可以得到验证码的位置信息 【4x02】验证码坐标解析函数123456def get_points(self, captcha_result): # 超级鹰识别结果以字符串形式返回,每个坐标都以|分隔 groups = captcha_result.get('pic_str').split('|') # 将坐标信息变成列表的形式 locations = [[int(number) for number in group.split(',')] for group in groups] return locations get_points() 方法将超级鹰的验证码识别结果变成列表的形式 【4x03】模拟点击验证码函数123456def touch_click_words(self, locations): element = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.login-pwd-code'))) # 循环点击正确验证码的坐标 for location in locations: print(location) ActionChains(self.browser).move_to_element_with_offset(element, location[0], location[1]).click().perform() 循环提取正确的验证码坐标信息,依次点击验证码 【5x00】登录模块123def login(self): submit = self.wait.until(EC.element_to_be_clickable((By.ID, 'J-login'))) submit.click() 分析页面,找到登陆按钮的 ID 为 J-login,调用 click() 方法模拟点击按钮实现登录 【6x00】完整代码【6x01】12306.py123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133# =============================================# --*-- coding: utf-8 --*--# @Time : 2019-10-21# @Author : TRHX# @Blog : www.itrhx.com# @CSDN : https://blog.csdn.net/qq_36759224# @FileName: 12306.py# @Software: PyCharm# =============================================import timefrom io import BytesIOfrom PIL import Imagefrom selenium import webdriverfrom selenium.webdriver.chrome.options import Optionsfrom selenium.webdriver import ActionChainsfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECfrom chaojiying import ChaojiyingClientfrom selenium.common.exceptions import TimeoutException# 12306账号密码USERNAME = '155********'PASSWORD = '***********'# 超级鹰打码平台账号密码CHAOJIYING_USERNAME = '********'CHAOJIYING_PASSWORD = '********'# 超级鹰打码平台软件IDCHAOJIYING_SOFT_ID = '******'# 验证码类型CHAOJIYING_KIND = '9004'class CrackTouClick(): def __init__(self): self.url = 'https://kyfw.12306.cn/otn/resources/login.html' # path是谷歌浏览器驱动的目录,如果已经将目录添加到系统变量,则不用设置此路径 path = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe' chrome_options = Options() chrome_options.add_argument('--start-maximized') self.browser = webdriver.Chrome(executable_path=path, chrome_options=chrome_options) self.wait = WebDriverWait(self.browser, 20) self.username = USERNAME self.password = PASSWORD self.chaojiying = ChaojiyingClient(CHAOJIYING_USERNAME, CHAOJIYING_PASSWORD, CHAOJIYING_SOFT_ID) def crack(self): # 调用账号密码输入函数 self.get_input_element() # 调用验证码图片剪裁函数 image = self.get_touclick_image() bytes_array = BytesIO() image.save(bytes_array, format='PNG') # 利用超级鹰打码平台的 API PostPic() 方法把图片发送给超级鹰后台,发送的图像是字节流格式,返回的结果是一个JSON result = self.chaojiying.PostPic(bytes_array.getvalue(), CHAOJIYING_KIND) print(result) # 调用验证码坐标解析函数 locations = self.get_points(result) # 调用模拟点击验证码函数 self.touch_click_words(locations) # 调用模拟点击登录函数 self.login() try: # 查找是否出现用户的姓名,若出现表示登录成功 success = self.wait.until(EC.text_to_be_present_in_element((By.CSS_SELECTOR, '.welcome-name'), '谭先生')) print(success) cc = self.browser.find_element(By.CSS_SELECTOR, '.welcome-name') print('用户' + cc.text + '登录成功') # 若没有出现表示登录失败,继续重试,超级鹰会返回本次识别的分值 except TimeoutException: self.chaojiying.ReportError(result['pic_id']) self.crack() # 账号密码输入函数 def get_input_element(self): # 登录页面发送请求 self.browser.get(self.url) # 登录页面默认是扫码登录,所以首先要点击账号登录 login = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.login-hd-account'))) login.click() time.sleep(3) # 查找到账号密码输入位置的元素 username = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'input#J-userName'))) password = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'input#J-password'))) # 输入账号密码 username.send_keys(self.username) password.send_keys(self.password) # 验证码图片剪裁函数 def get_touclick_image(self, name='12306.png'): # 获取验证码的位置 element = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.login-pwd-code'))) time.sleep(3) location = element.location size = element.size top, bottom, left, right = location['y'], location['y'] + size['height'], location['x'], location['x'] + size[ 'width'] # 先对整个页面截图 screenshot = self.browser.get_screenshot_as_png() screenshot = Image.open(BytesIO(screenshot)) # 根据验证码坐标信息,剪裁出验证码图片 captcha = screenshot.crop((left, top, right, bottom)) captcha.save(name) return captcha # 验证码坐标解析函数,分析超级鹰返回的坐标 def get_points(self, captcha_result): # 超级鹰识别结果以字符串形式返回,每个坐标都以|分隔 groups = captcha_result.get('pic_str').split('|') # 将坐标信息变成列表的形式 locations = [[int(number) for number in group.split(',')] for group in groups] return locations # 模拟点击验证码函数 def touch_click_words(self, locations): element = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.login-pwd-code'))) # 循环点击正确验证码的坐标 for location in locations: print(location) ActionChains(self.browser).move_to_element_with_offset(element, location[0], location[1]).click().perform() # 模拟点击登录函数 def login(self): submit = self.wait.until(EC.element_to_be_clickable((By.ID, 'J-login'))) submit.click()if __name__ == '__main__': crack = CrackTouClick() crack.crack() 【6x02】chaojiying.py12345678910111213141516171819202122232425262728293031323334353637383940414243import requestsfrom hashlib import md5class ChaojiyingClient(object): def __init__(self, username, password, soft_id): self.username = username password = password.encode('utf8') self.password = md5(password).hexdigest() self.soft_id = soft_id self.base_params = { 'user': self.username, 'pass2': self.password, 'softid': self.soft_id, } self.headers = { 'Connection': 'Keep-Alive', 'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)', } def PostPic(self, im, codetype): \"\"\" im: 图片字节 codetype: 题目类型 参考 http://www.chaojiying.com/price.html \"\"\" params = { 'codetype': codetype, } params.update(self.base_params) files = {'userfile': ('ccc.jpg', im)} r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, files=files, headers=self.headers) return r.json() def ReportError(self, im_id): \"\"\" im_id:报错题目的图片ID \"\"\" params = { 'id': im_id, } params.update(self.base_params) r = requests.post('http://upload.chaojiying.net/Upload/ReportError.php', data=params, headers=self.headers) return r.json() 【7x00】效果实现动图最终实现效果图:(关键信息已经过打码处理)","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫实战","slug":"Python3-学习笔记/爬虫实战","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫实战/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"12306","slug":"12306","permalink":"https://www.itrhx.com/tags/12306/"}]},{"title":"Python3 爬虫实战 — 模拟登陆哔哩哔哩【滑动验证码对抗】","slug":"A56-pyspider-bilibili-login","date":"2019-10-21T04:26:46.838Z","updated":"2019-10-21T13:33:57.637Z","comments":true,"path":"2019/10/21/A56-pyspider-bilibili-login/","link":"","permalink":"https://www.itrhx.com/2019/10/21/A56-pyspider-bilibili-login/","excerpt":"登陆时间:2019-10-21实现难度:★★★☆☆☆请求链接:https://passport.bilibili.com/login实现目标:模拟登陆哔哩哔哩,攻克滑动验证码涉及知识:滑动验证码的攻克、自动化测试工具 Selenium 的使用完整代码:https://github.com/TRHX/Python3-Spider-Practice/tree/master/bilibili-login其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278","text":"登陆时间:2019-10-21实现难度:★★★☆☆☆请求链接:https://passport.bilibili.com/login实现目标:模拟登陆哔哩哔哩,攻克滑动验证码涉及知识:滑动验证码的攻克、自动化测试工具 Selenium 的使用完整代码:https://github.com/TRHX/Python3-Spider-Practice/tree/master/bilibili-login其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278 【1x00】思维导图 利用自动化测试工具 Selenium 直接模拟人的行为方式来完成验证 分析页面,想办法找到滑动验证码的完整图片、带有缺口的图片和需要滑动的图片 对比原始的图片和带缺口的图片的像素,像素不同的地方就是缺口位置 计算出滑块缺口的位置,得到所需要滑动的距离 拖拽时要模仿人的行为,由于有个对准过程,所以要构造先快后慢的运动轨迹 最后利用 Selenium 进行对滑块的拖拽 【2x00】登陆模块【2x01】初始化函数12345678910111213def init(): global url, browser, username, password, wait url = 'https://passport.bilibili.com/login' # path是谷歌浏览器驱动的目录,如果已经将目录添加到系统变量,则不用设置此路径 path = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe' chrome_options = Options() chrome_options.add_argument('--start-maximized') browser = webdriver.Chrome(executable_path=path, chrome_options=chrome_options) # 你的哔哩哔哩用户名 username = '155********' # 你的哔哩哔哩登陆密码 password = '***********' wait = WebDriverWait(browser, 20) global 关键字定义了发起请求的url、用户名、密码等全局变量,随后是登录页面url、谷歌浏览器驱动的目录path、实例化 Chrome 浏览器、设置浏览器分辨率最大化、用户名、密码、WebDriverWait() 方法设置等待超时 【2x02】登陆函数12345678910111213141516def login(): browser.get(url) # 获取用户名输入框 user = wait.until(EC.presence_of_element_located((By.ID, 'login-username'))) # 获取密码输入框 passwd = wait.until(EC.presence_of_element_located((By.ID, 'login-passwd'))) # 输入用户名 user.send_keys(username) # 输入密码 passwd.send_keys(password) # 获取登录按钮 login_btn = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'a.btn.btn-login'))) # 随机暂停几秒 time.sleep(random.random() * 3) # 点击登陆按钮 login_btn.click() 等待用户名输入框和密码输入框对应的 ID 节点加载出来 获取这两个节点,用户名输入框 id="login-username",密码输入框 id="login-passwd" 调用 send_keys() 方法输入用户名和密码 获取登录按钮 class="btn btn-login" 随机产生一个数并将其扩大三倍作为暂停时间 最后调用 click() 方法实现登录按钮的点击 【3x00】验证码处理模块【3x01】验证码元素查找函数12345678910111213141516171819202122def find_element(): # 获取带有缺口的图片 c_background = wait.until( EC.presence_of_element_located((By.CSS_SELECTOR, 'canvas.geetest_canvas_bg.geetest_absolute'))) # 获取需要滑动的图片 c_slice = wait.until( EC.presence_of_element_located((By.CSS_SELECTOR, 'canvas.geetest_canvas_slice.geetest_absolute'))) # 获取完整的图片 c_full_bg = wait.until( EC.presence_of_element_located((By.CSS_SELECTOR, 'canvas.geetest_canvas_fullbg.geetest_fade.geetest_absolute'))) # 隐藏需要滑动的图片 hide_element(c_slice) # 保存带有缺口的图片 save_screenshot(c_background, 'back') # 显示需要滑动的图片 show_element(c_slice) # 保存需要滑动的图片 save_screenshot(c_slice, 'slice') # 显示完整的图片 show_element(c_full_bg) # 保存完整的图片 save_screenshot(c_full_bg, 'full') 获取验证码的三张图片,分别是完整的图片、带有缺口的图片和需要滑动的图片 分析页面代码,三张图片是由 3 个 canvas 组成,3 个 canvas 元素包含 CSS display 属性,display:block 为可见,display:none 为不可见,在分别获取三张图片时要将其他两张图片设置为 display:none,这样做才能单独提取到每张图片 定位三张图片的 class 分别为:带有缺口的图片(c_background):geetest_canvas_bg geetest_absolute、需要滑动的图片(c_slice):geetest_canvas_slice geetest_absolute、完整图片(c_full_bg):geetest_canvas_fullbg geetest_fade geetest_absolute 最后传值给 save_screenshot() 函数,进一步对验证码进行处理 【3x02】元素可见性设置函数12345678# 设置元素不可见def hide_element(element): browser.execute_script(\"arguments[0].style=arguments[1]\", element, \"display: none;\")# 设置元素可见def show_element(element): browser.execute_script(\"arguments[0].style=arguments[1]\", element, \"display: block;\") 【3x03】验证码截图函数123456789101112131415161718192021222324def save_screenshot(obj, name): try: # 首先对出现验证码后的整个页面进行截图保存 pic_url = browser.save_screenshot('.\\\\bilibili.png') print(\"%s:截图成功!\" % pic_url) # 计算传入的obj,也就是三张图片的位置信息 left = obj.location['x'] top = obj.location['y'] right = left + obj.size['width'] bottom = top + obj.size['height'] # 打印输出一下每一张图的位置信息 print('图:' + name) print('Left %s' % left) print('Top %s' % top) print('Right %s' % right) print('Bottom %s' % bottom) print('') # 在整个页面截图的基础上,根据位置信息,分别剪裁出三张验证码图片并保存 im = Image.open('.\\\\bilibili.png') im = im.crop((left, top, right, bottom)) file_name = 'bili_' + name + '.png' im.save(file_name) except BaseException as msg: print(\"%s:截图失败!\" % msg) location 属性可以返回该图片对象在浏览器中的位置,坐标轴是以屏幕左上角为原点,x轴向右递增,y轴向下递增 size 属性可以返回该图片对象的高度和宽度,由此可以得到验证码的位置信息 首先调用 save_screenshot() 属性对整个页面截图并保存 然后向 crop() 方法传入验证码的位置信息,由位置信息再对验证码进行剪裁并保存 【4x00】验证码滑动模块【4x01】滑动主函数123456def slide(): distance = get_distance(Image.open('.\\\\bili_back.png'), Image.open('.\\\\bili_full.png')) print('计算偏移量为:%s Px' % distance) trace = get_trace(distance - 5) move_to_gap(trace) time.sleep(3) 向 get_distance() 函数传入完整的图片和缺口图片,计算滑块需要滑动的距离,再把距离信息传入 get_trace() 函数,构造滑块的移动轨迹,最后根据轨迹信息调用 move_to_gap() 函数移动滑块完成验证 【4x02】缺口位置寻找函数123456789101112def is_pixel_equal(bg_image, fullbg_image, x, y): # 获取两张图片对应像素点的RGB数据 bg_pixel = bg_image.load()[x, y] fullbg_pixel = fullbg_image.load()[x, y] # 设定一个阈值 threshold = 60 # 比较两张图 RGB 的绝对值是否均小于定义的阈值 if (abs(bg_pixel[0] - fullbg_pixel[0] < threshold) and abs(bg_pixel[1] - fullbg_pixel[1] < threshold) and abs( bg_pixel[2] - fullbg_pixel[2] < threshold)): return True else: return False 将完整图片和缺口图片两个对象分别赋值给变量 bg_image 和 fullbg_image,接下来对比图片获取缺口。遍历图片的每个坐标点,获取两张图片对应像素点的 RGB 数据,判断像素的各个颜色之差,abs() 用于取绝对值,比较两张图 RGB 的绝对值是否均小于定义的阈值 threshold,如果绝对值均在阈值之内,则代表像素点相同,继续遍历,否则代表不相同的像素点,即缺口的位置 【4x03】计算滑块移动距离函数123456789def get_distance(bg_image, fullbg_image): # 滑块的初始位置 distance = 60 # 遍历两张图片的每个像素 for i in range(distance, fullbg_image.size[0]): for j in range(fullbg_image.size[1]): # 调用缺口位置寻找函数 if not is_pixel_equal(fullbg_image, bg_image, i, j): return i get_distance() 方法即获取缺口位置的方法,此方法的参数是两张图片,一张为完整的图片,另一张为带缺口的图片,distance 为滑块的初始位置,遍历两张图片的每个像素,利用 is_pixel_equal() 缺口位置寻找函数判断两张图片同一位置的像素是否相同,若不相同则返回该点的值 【4x04】构造移动轨迹函数1234567891011121314151617181920def get_trace(distance): trace = [] # 设置加速距离为总距离的4/5 faster_distance = distance * (4 / 5) # 设置初始位置、初始速度、时间间隔 start, v0, t = 0, 0, 0.1 while start < distance: if start < faster_distance: a = 10 else: a = -10 # 位移 move = v0 * t + 1 / 2 * a * t * t # 当前时刻的速度 v = v0 + a * t v0 = v start += move trace.append(round(move)) # trace 记录了每个时间间隔移动了多少位移 return trace get_trace() 方法传入的参数为移动的总距离,返回的是运动轨迹,运动轨迹用 trace 表示,它是一个列表,列表的每个元素代表每次移动多少距离,利用 Selenium 进行对滑块的拖拽时要模仿人的行为,由于有个对准过程,所以是先快后慢,匀速移动、随机速度移动都不会成功,因此要设置一个加速和减速的距离,这里设置加速距离 faster_distance 是总距离 distance 的4/5倍,滑块滑动的加速度用 a 来表示,当前速度用 v 表示,初速度用 v0 表示,位移用 move 表示,所需时间用 t 表示,它们之间满足以下关系: 12move = v0 * t + 0.5 * a * t * t v = v0 + a * t 设置初始位置、初始速度、时间间隔分别为0, 0, 0.1,加速阶段和减速阶段的加速度分别设置为10和-10,直到运动轨迹达到总距离时,循环终止,最后得到的 trace 记录了每个时间间隔移动了多少位移,这样滑块的运动轨迹就得到了 【4x05】模拟拖动函数123456789101112def move_to_gap(trace): # 获取滑动按钮 slider = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'div.geetest_slider_button'))) # 点击并拖动滑块 ActionChains(browser).click_and_hold(slider).perform() # 遍历运动轨迹获取每小段位移距离 for x in trace: # 移动此位移 ActionChains(browser).move_by_offset(xoffset=x, yoffset=0).perform() time.sleep(0.5) # 释放鼠标 ActionChains(browser).release().perform() 传入的参数为运动轨迹,首先查找到滑动按钮,然后调用 ActionChains 的 click_and_hold() 方法按住拖动底部滑块,perform() 方法用于执行,遍历运动轨迹获取每小段位移距离,调用 move_by_offset() 方法移动此位移,最后调用 release() 方法松开鼠标即可 【5x00】完整代码123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197# =============================================# --*-- coding: utf-8 --*--# @Time : 2019-10-21# @Author : TRHX# @Blog : www.itrhx.com# @CSDN : https://blog.csdn.net/qq_36759224# @FileName: bilibili.py# @Software: PyCharm# =============================================from selenium import webdriverfrom selenium.webdriver.chrome.options import Optionsfrom selenium.webdriver.support.wait import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECfrom selenium.webdriver.common.by import Byfrom selenium.webdriver import ActionChainsimport timeimport randomfrom PIL import Image# 初始化函数def init(): global url, browser, username, password, wait url = 'https://passport.bilibili.com/login' # path是谷歌浏览器驱动的目录,如果已经将目录添加到系统变量,则不用设置此路径 path = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe' chrome_options = Options() chrome_options.add_argument('--start-maximized') browser = webdriver.Chrome(executable_path=path, chrome_options=chrome_options) # 你的哔哩哔哩用户名 username = '155********' # 你的哔哩哔哩登录密码 password = '***********' wait = WebDriverWait(browser, 20)# 登录函数def login(): browser.get(url) # 获取用户名输入框 user = wait.until(EC.presence_of_element_located((By.ID, 'login-username'))) # 获取密码输入框 passwd = wait.until(EC.presence_of_element_located((By.ID, 'login-passwd'))) # 输入用户名 user.send_keys(username) # 输入密码 passwd.send_keys(password) # 获取登录按钮 login_btn = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'a.btn.btn-login'))) # 随机暂停几秒 time.sleep(random.random() * 3) # 点击登陆按钮 login_btn.click()# 验证码元素查找函数def find_element(): # 获取带有缺口的图片 c_background = wait.until( EC.presence_of_element_located((By.CSS_SELECTOR, 'canvas.geetest_canvas_bg.geetest_absolute'))) # 获取需要滑动的图片 c_slice = wait.until( EC.presence_of_element_located((By.CSS_SELECTOR, 'canvas.geetest_canvas_slice.geetest_absolute'))) # 获取完整的图片 c_full_bg = wait.until( EC.presence_of_element_located((By.CSS_SELECTOR, 'canvas.geetest_canvas_fullbg.geetest_fade.geetest_absolute'))) # 隐藏需要滑动的图片 hide_element(c_slice) # 保存带有缺口的图片 save_screenshot(c_background, 'back') # 显示需要滑动的图片 show_element(c_slice) # 保存需要滑动的图片 save_screenshot(c_slice, 'slice') # 显示完整的图片 show_element(c_full_bg) # 保存完整的图片 save_screenshot(c_full_bg, 'full')# 设置元素不可见def hide_element(element): browser.execute_script(\"arguments[0].style=arguments[1]\", element, \"display: none;\")# 设置元素可见def show_element(element): browser.execute_script(\"arguments[0].style=arguments[1]\", element, \"display: block;\")# 验证码截图函数def save_screenshot(obj, name): try: # 首先对出现验证码后的整个页面进行截图保存 pic_url = browser.save_screenshot('.\\\\bilibili.png') print(\"%s:截图成功!\" % pic_url) # 计算传入的obj,也就是三张图片的位置信息 left = obj.location['x'] top = obj.location['y'] right = left + obj.size['width'] bottom = top + obj.size['height'] # 打印输出一下每一张图的位置信息 print('图:' + name) print('Left %s' % left) print('Top %s' % top) print('Right %s' % right) print('Bottom %s' % bottom) print('') # 在整个页面截图的基础上,根据位置信息,分别剪裁出三张验证码图片并保存 im = Image.open('.\\\\bilibili.png') im = im.crop((left, top, right, bottom)) file_name = 'bili_' + name + '.png' im.save(file_name) except BaseException as msg: print(\"%s:截图失败!\" % msg)# 滑动模块的主函数def slide(): distance = get_distance(Image.open('.\\\\bili_back.png'), Image.open('.\\\\bili_full.png')) print('计算偏移量为:%s Px' % distance) trace = get_trace(distance - 5) move_to_gap(trace) time.sleep(3)# 计算滑块移动距离函数def get_distance(bg_image, fullbg_image): # 滑块的初始位置 distance = 60 # 遍历两张图片的每个像素 for i in range(distance, fullbg_image.size[0]): for j in range(fullbg_image.size[1]): # 调用缺口位置寻找函数 if not is_pixel_equal(fullbg_image, bg_image, i, j): return i# 缺口位置寻找函数def is_pixel_equal(bg_image, fullbg_image, x, y): # 获取两张图片对应像素点的RGB数据 bg_pixel = bg_image.load()[x, y] fullbg_pixel = fullbg_image.load()[x, y] # 设定一个阈值 threshold = 60 # 比较两张图 RGB 的绝对值是否均小于定义的阈值 if (abs(bg_pixel[0] - fullbg_pixel[0] < threshold) and abs(bg_pixel[1] - fullbg_pixel[1] < threshold) and abs( bg_pixel[2] - fullbg_pixel[2] < threshold)): return True else: return False# 构造移动轨迹函数def get_trace(distance): trace = [] # 设置加速距离为总距离的4/5 faster_distance = distance * (4 / 5) # 设置初始位置、初始速度、时间间隔 start, v0, t = 0, 0, 0.1 while start < distance: if start < faster_distance: a = 10 else: a = -10 # 位移 move = v0 * t + 1 / 2 * a * t * t # 当前时刻的速度 v = v0 + a * t v0 = v start += move trace.append(round(move)) # trace 记录了每个时间间隔移动了多少位移 return trace# 模拟拖动函数def move_to_gap(trace): # 获取滑动按钮 slider = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'div.geetest_slider_button'))) # 点击并拖动滑块 ActionChains(browser).click_and_hold(slider).perform() # 遍历运动轨迹获取每小段位移距离 for x in trace: # 移动此位移 ActionChains(browser).move_by_offset(xoffset=x, yoffset=0).perform() time.sleep(0.5) # 释放鼠标 ActionChains(browser).release().perform()if __name__ == '__main__': init() login() find_element() slide() 【6x00】效果实现动图最终实现效果图:(关键信息已经过打码处理)","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫实战","slug":"Python3-学习笔记/爬虫实战","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫实战/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"哔哩哔哩","slug":"哔哩哔哩","permalink":"https://www.itrhx.com/tags/哔哩哔哩/"}]},{"title":"Python3 爬虫实战 — 虎扑论坛步行街","slug":"A55-pyspider-hupu","date":"2019-10-12T15:28:23.380Z","updated":"2019-10-21T04:08:46.598Z","comments":true,"path":"2019/10/12/A55-pyspider-hupu/","link":"","permalink":"https://www.itrhx.com/2019/10/12/A55-pyspider-hupu/","excerpt":"爬取时间:2019-10-12爬取难度:★★☆☆☆☆请求链接:https://bbs.hupu.com/bxj爬取目标:爬取虎扑论坛步行街的帖子,包含主题,作者,发布时间等,数据保存到 MongoDB 数据库涉及知识:请求库 requests、解析库 Beautiful Soup、数据库 MongoDB 的操作完整代码:https://github.com/TRHX/Python3-Spider-Practice/tree/master/hupu其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278","text":"爬取时间:2019-10-12爬取难度:★★☆☆☆☆请求链接:https://bbs.hupu.com/bxj爬取目标:爬取虎扑论坛步行街的帖子,包含主题,作者,发布时间等,数据保存到 MongoDB 数据库涉及知识:请求库 requests、解析库 Beautiful Soup、数据库 MongoDB 的操作完整代码:https://github.com/TRHX/Python3-Spider-Practice/tree/master/hupu其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278 【1x00】循环爬取网页模块观察虎扑论坛步行街分区,请求地址为:https://bbs.hupu.com/bxj 第一页:https://bbs.hupu.com/bxj 第二页:https://bbs.hupu.com/bxj-2 第三页:https://bbs.hupu.com/bxj-3 不难发现,每增加一页,只需要添加 -页数 参数即可,最后一页是第 50 页,因此可以利用 for 循环依次爬取,定义一个 get_pages() 函数,返回初始化 Beautiful Soup 的对象 page_soup,方便后面的解析函数调用 虽然一共有 50 页,但是当用户访问第 10 页以后的页面的时候,会要求登录虎扑,不然就没法查看,而且登录时会出现智能验证,所以程序只爬取前 10 页的数据 123456789101112def get_pages(page_url): headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36' } response = requests.get(url=page_url, headers=headers) page_soup = BeautifulSoup(response.text, 'lxml') return page_soupif __name__ == '__main__': for i in range(1, 11): url = 'https://bbs.hupu.com/bxj-' + str(i) soup = get_pages(url) 【2x00】解析模块使用 Beautiful Soup 对网页各个信息进行提取,最后将这些信息放进一个列表里,然后调用列表的 .append() 方法,再将每条帖子的列表依次加到另一个新列表里,最终返回的是类似于如下形式的列表: 1[['帖子1', '作者1'], ['帖子2', '作者2'], ['帖子3', '作者3']] 这样做的目的是:方便 MongoDB 依次储存每一条帖子的信息 123456789101112131415161718192021222324252627282930313233343536373839def parse_pages(page_soup): data_list = [] all_list = page_soup.find('ul', class_='for-list') post_list = all_list.find_all('li') # print(result_list) for post in post_list: # 帖子名称 post_title = post.find('a', class_='truetit').text # print(post_title) # 帖子链接 post_url = 'https://bbs.hupu.com' + post.find('a', class_='truetit')['href'] # print(post_url) # 作者 author = post.select('.author > a')[0].text # print(author) # 作者主页 author_url = post.select('.author > a')[0]['href'] # print(author_url) # 发布日期 post_date = post.select('.author > a')[1].text # print(post_date) reply_view = post.find('span', class_='ansour').text # 回复数 post_reply = reply_view.split('/')[0].strip() # print(post_reply) # 浏览量 post_view = reply_view.split('/')[1].strip() # print(post_view) # 最后回复时间 last_data = post.select('.endreply > a')[0].text # print(last_data) # 最后回复用户 last_user = post.select('.endreply > span')[0].text # print(last_user) data_list.append([post_title, post_url, author, author_url, post_date, post_reply, post_view, last_data, last_user]) # print(data_list) return data_list 【3x00】MongoDB 数据储存模块首先使用 MongoClient() 方法,向其传入地址参数 host 和 端口参数 port,指定数据库为 hupu,集合为 bxj 将解析函数返回的列表传入到储存函数,依次循环该列表,对每一条帖子的信息进行提取并储存 1234567891011121314151617def mongodb(data_list): client = MongoClient('localhost', 27017) db = client.hupu collection = db.bxj for data in data_list: bxj = { '帖子名称': data[0], '帖子链接': data[1], '作者': data[2], '作者主页': data[3], '发布日期': str(data[4]), '回复数': data[5], '浏览量': data[6], '最后回复时间': str(data[7]), '最后回复用户': data[8] } collection.insert_one(bxj) 【4x00】完整代码1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495# =============================================# --*-- coding: utf-8 --*--# @Time : 2019-10-12# @Author : TRHX# @Blog : www.itrhx.com# @CSDN : https://blog.csdn.net/qq_36759224# @FileName: hupu.py# @Software: PyCharm# =============================================import requestsimport timeimport randomfrom pymongo import MongoClientfrom bs4 import BeautifulSoupdef get_pages(page_url): headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36' } response = requests.get(url=page_url, headers=headers) page_soup = BeautifulSoup(response.text, 'lxml') return page_soupdef parse_pages(page_soup): data_list = [] all_list = page_soup.find('ul', class_='for-list') post_list = all_list.find_all('li') # print(result_list) for post in post_list: # 帖子名称 post_title = post.find('a', class_='truetit').text # print(post_title) # 帖子链接 post_url = 'https://bbs.hupu.com' + post.find('a', class_='truetit')['href'] # print(post_url) # 作者 author = post.select('.author > a')[0].text # print(author) # 作者主页 author_url = post.select('.author > a')[0]['href'] # print(author_url) # 发布日期 post_date = post.select('.author > a')[1].text # print(post_date) reply_view = post.find('span', class_='ansour').text # 回复数 post_reply = reply_view.split('/')[0].strip() # print(post_reply) # 浏览量 post_view = reply_view.split('/')[1].strip() # print(post_view) # 最后回复时间 last_data = post.select('.endreply > a')[0].text # print(last_data) # 最后回复用户 last_user = post.select('.endreply > span')[0].text # print(last_user) data_list.append([post_title, post_url, author, author_url, post_date, post_reply, post_view, last_data, last_user]) # print(data_list) return data_listdef mongodb(data_list): client = MongoClient('localhost', 27017) db = client.hupu collection = db.bxj for data in data_list: bxj = { '帖子名称': data[0], '帖子链接': data[1], '作者': data[2], '作者主页': data[3], '发布日期': str(data[4]), '回复数': data[5], '浏览量': data[6], '最后回复时间': str(data[7]), '最后回复用户': data[8] } collection.insert_one(bxj)if __name__ == '__main__': for i in range(1, 11): url = 'https://bbs.hupu.com/bxj-' + str(i) soup = get_pages(url) result_list = parse_pages(soup) mongodb(result_list) print('第', i, '页数据爬取完毕!') time.sleep(random.randint(3, 10)) print('前10页所有数据爬取完毕!') 【5x00】数据截图一共爬取到 1180 条数据: 【6x00】程序不足的地方程序只能爬取前 10 页的数据,因为虎扑论坛要求从第 11 页开始,必须登录账号才能查看,并且登录时会有智能验证,可以使用自动化测试工具 Selenium 模拟登录账号后再进行爬取。","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫实战","slug":"Python3-学习笔记/爬虫实战","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫实战/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"虎扑论坛","slug":"虎扑论坛","permalink":"https://www.itrhx.com/tags/虎扑论坛/"}]},{"title":"Python3 爬虫实战 — 安居客武汉二手房","slug":"A54-pyspider-anjuke","date":"2019-10-09T15:02:42.994Z","updated":"2019-10-21T04:05:48.158Z","comments":true,"path":"2019/10/09/A54-pyspider-anjuke/","link":"","permalink":"https://www.itrhx.com/2019/10/09/A54-pyspider-anjuke/","excerpt":"爬取时间:2019-10-09爬取难度:★★☆☆☆☆请求链接:https://wuhan.anjuke.com/sale/爬取目标:爬取武汉二手房每一条售房信息,包含地理位置、价格、面积等,保存为 CSV 文件涉及知识:请求库 requests、解析库 Beautiful Soup、CSV 文件储存、列表操作、分页判断完整代码:https://github.com/TRHX/Python3-Spider-Practice/tree/master/anjuke其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278","text":"爬取时间:2019-10-09爬取难度:★★☆☆☆☆请求链接:https://wuhan.anjuke.com/sale/爬取目标:爬取武汉二手房每一条售房信息,包含地理位置、价格、面积等,保存为 CSV 文件涉及知识:请求库 requests、解析库 Beautiful Soup、CSV 文件储存、列表操作、分页判断完整代码:https://github.com/TRHX/Python3-Spider-Practice/tree/master/anjuke其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278 【1x00】页面整体分析分析 安居客武汉二手房页面,这次爬取实战准备使用 BeautifulSoup 解析库,熟练 BeautifulSoup 解析库的用法,注意到该页面与其他页面不同的是,不能一次性看到到底有多少页,以前知道一共有多少页,直接一个循环爬取就行了,虽然可以通过改变 url 来尝试找到最后一页,但是这样就显得不程序员了😂,因此可以通过 BeautifulSoup 解析 下一页按钮,提取到下一页的 url,直到没有 下一页按钮 这个元素为止,从而实现所有页面的爬取,剩下的信息提取和储存就比较简单了 【2x00】解析模块分析页面,可以发现每条二手房信息都是包含在 <li> 标签内的,因此可以使用 BeautifulSoup 解析页面得到所有的 <li> 标签,然后再循环访问每个 <li> 标签,依次解析得到每条二手房的各种信息 1234567891011121314151617181920212223242526272829303132333435363738394041def parse_pages(url, num): response = requests.get(url=url, headers=headers) soup = BeautifulSoup(response.text, 'lxml') result_list = soup.find_all('li', class_='list-item') # print(len(result_list)) for result in result_list: # 标题 title = result.find('a', class_='houseListTitle').text.strip() # print(title) # 户型 layout = result.select('.details-item > span')[0].text # print(layout) # 面积 cover = result.select('.details-item > span')[1].text # print(cover) # 楼层 floor = result.select('.details-item > span')[2].text # print(floor) # 建造年份 year = result.select('.details-item > span')[3].text # print(year) # 单价 unit_price = result.find('span', class_='unit-price').text.strip() # print(unit_price) # 总价 total_price = result.find('span', class_='price-det').text.strip() # print(total_price) # 关键字 keyword = result.find('div', class_='tags-bottom').text.strip() # print(keyword) # 地址 address = result.find('span', class_='comm-address').text.replace(' ', '').replace('\\n', '') # print(address) # 详情页url details_url = result.find('a', class_='houseListTitle')['href'] # print(details_url)if __name__ == '__main__': start_num = 0 start_url = 'https://wuhan.anjuke.com/sale/' parse_pages(start_url, start_num) 【3x00】循环爬取模块前面已经分析过,该网页是无法一下就能看到一共有多少页的,尝试找到最后一页,发现一共有50页,那么此时就可以搞个循环,一直到第50页就行了,但是如果有一天页面数增加了呢,那么代码的可维护性就不好了,我们可以观察 下一页按钮 ,当存在下一页的时候,是 <a> 标签,并且带有下一页的 URL,不存在下一页的时候是 <i> 标签,因此可以写个 if 语句,判断是否存在此 <a> 标签,若存在,表示有下一页,然后提取其 href 属性并传给解析模块,实现后面所有页面的信息提取,此外,由于安居客有反爬系统,我们还可以利用 Python中的 random.randint() 方法,在两个数值之间随机取一个数,传入 time.sleep() 方法,实现随机暂停爬取 12345678910# 判断是否还有下一页next_url = soup.find_all('a', class_='aNxt')if len(next_url) != 0: num += 1 print('第' + str(num) + '页数据爬取完毕!') # 3-60秒之间随机暂停 time.sleep(random.randint(3, 60)) parse_pages(next_url[0].attrs['href'], num)else: print('所有数据爬取完毕!') 【4x00】数据储存模块数据储存比较简单,将每个二手房信息组成一个列表,依次写入到 anjuke.csv 文件中即可 1234results = [title, layout, cover, floor, year, unit_price, total_price, keyword, address, details_url]with open('anjuke.csv', 'a', newline='', encoding='utf-8-sig') as f: w = csv.writer(f) w.writerow(results) 【5x00】完整代码123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081# =============================================# --*-- coding: utf-8 --*--# @Time : 2019-10-09# @Author : TRHX# @Blog : www.itrhx.com# @CSDN : https://blog.csdn.net/qq_36759224# @FileName: anjuke.py# @Software: PyCharm# =============================================import requestsimport timeimport csvimport randomfrom bs4 import BeautifulSoupheaders = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'}def parse_pages(url, num): response = requests.get(url=url, headers=headers) soup = BeautifulSoup(response.text, 'lxml') result_list = soup.find_all('li', class_='list-item') # print(len(result_list)) for result in result_list: # 标题 title = result.find('a', class_='houseListTitle').text.strip() # print(title) # 户型 layout = result.select('.details-item > span')[0].text # print(layout) # 面积 cover = result.select('.details-item > span')[1].text # print(cover) # 楼层 floor = result.select('.details-item > span')[2].text # print(floor) # 建造年份 year = result.select('.details-item > span')[3].text # print(year) # 单价 unit_price = result.find('span', class_='unit-price').text.strip() # print(unit_price) # 总价 total_price = result.find('span', class_='price-det').text.strip() # print(total_price) # 关键字 keyword = result.find('div', class_='tags-bottom').text.strip() # print(keyword) # 地址 address = result.find('span', class_='comm-address').text.replace(' ', '').replace('\\n', '') # print(address) # 详情页url details_url = result.find('a', class_='houseListTitle')['href'] # print(details_url) results = [title, layout, cover, floor, year, unit_price, total_price, keyword, address, details_url] with open('anjuke.csv', 'a', newline='', encoding='utf-8-sig') as f: w = csv.writer(f) w.writerow(results) # 判断是否还有下一页 next_url = soup.find_all('a', class_='aNxt') if len(next_url) != 0: num += 1 print('第' + str(num) + '页数据爬取完毕!') # 3-60秒之间随机暂停 time.sleep(random.randint(3, 60)) parse_pages(next_url[0].attrs['href'], num) else: print('所有数据爬取完毕!')if __name__ == '__main__': with open('anjuke.csv', 'a', newline='', encoding='utf-8-sig') as fp: writer = csv.writer(fp) writer.writerow(['标题', '户型', '面积', '楼层', '建造年份', '单价', '总价', '关键字', '地址', '详情页地址']) start_num = 0 start_url = 'https://wuhan.anjuke.com/sale/' parse_pages(start_url, start_num) 【6x00】数据截图 【7x00】程序不足的地方 虽然使用了随机暂停爬取的方法,但是在爬取了大约 20 页的数据后依然会出现验证页面,导致程序终止 原来设想的是可以由用户手动输入城市的拼音来查询不同城市的信息,方法是把用户输入的城市拼音和其他参数一起构造成一个 URL,然后对该 URL 发送请求,判断请求返回的代码,如果是 200 就代表可以访问,也就是用户输入的城市是正确的,然而发现即便是输入错误,该 URL 依然可以访问,只不过会跳转到一个正确的页面,没有搞清楚是什么原理,也就无法实现由用户输入城市来查询这个功能","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫实战","slug":"Python3-学习笔记/爬虫实战","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫实战/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"安居客","slug":"安居客","permalink":"https://www.itrhx.com/tags/安居客/"}]},{"title":"使用 Hexo-Git-Backup 插件备份你的 Hexo 博客","slug":"A53-hexo-backup","date":"2019-09-29T10:02:15.603Z","updated":"2019-12-29T07:19:49.434Z","comments":true,"path":"2019/09/29/A53-hexo-backup/","link":"","permalink":"https://www.itrhx.com/2019/09/29/A53-hexo-backup/","excerpt":"","text":"欢迎关注我的 CSDN 专栏:《个人博客搭建:Hexo+Github Pages》,从搭建到美化一条龙,帮你解决 Hexo 常见问题! 由于 Hexo 博客是静态托管的,所有的原始数据都保存在本地,如果哪一天电脑坏了,或者是误删了本地数据,那就是叫天天不应叫地地不灵了,此时定时备份就显得比较重要了,常见的备份方法有:打包数据保存到U盘、云盘或者其他地方,但是早就有大神开发了备份插件:hexo-git-backup ,只需要一个命令就可以将所有数据包括主题文件备份到 github 了 首先进入你博客目录,输入命令 hexo version 查看 Hexo 版本,如图所示,我的版本是 3.7.1: 安装备份插件,如果你的 Hexo 版本是 2.x.x,则使用以下命令安装: 1$ npm install hexo-git-backup@0.0.91 --save 如果你的 Hexo 版本是 3.x.x,则使用以下命令安装: 1$ npm install hexo-git-backup --save 到 Hexo 博客根目录的 _config.yml 配置文件里添加以下配置: 1234567backup: type: git theme: material-x-1.2.1 message: Back up my www.itrhx.com blog repository: github: git@github.com:TRHX/TRHX.github.io.git,backup coding: git@git.dev.tencent.com:TRHX/TRHX.git,backup 参数解释: theme:你要备份的主题名称 message:自定义提交信息 repository:仓库名,注意仓库地址后面要添加一个分支名,比如我就创建了一个 backup 分支 最后使用以下命令备份你的博客: 1$ hexo backup 或者使用以下简写命令也可以: 1$ hexo b 备份成功后可以在你的仓库分支下看到备份的原始文件:","categories":[{"name":"Hexo","slug":"Hexo","permalink":"https://www.itrhx.com/categories/Hexo/"}],"tags":[{"name":"Hexo","slug":"Hexo","permalink":"https://www.itrhx.com/tags/Hexo/"},{"name":"备份","slug":"备份","permalink":"https://www.itrhx.com/tags/备份/"}]},{"title":"Python3 爬虫实战 — 豆瓣电影TOP250","slug":"A52-pyspider-doubantop250","date":"2019-09-28T08:35:19.823Z","updated":"2019-10-21T04:01:29.248Z","comments":true,"path":"2019/09/28/A52-pyspider-doubantop250/","link":"","permalink":"https://www.itrhx.com/2019/09/28/A52-pyspider-doubantop250/","excerpt":"爬取时间:2019-09-27爬取难度:★★☆☆☆☆请求链接:https://movie.douban.com/top250 以及每部电影详情页爬取目标:爬取榜单上每一部电影详情页的数据,保存为 CSV 文件;下载所有电影海报到本地涉及知识:请求库 requests、解析库 lxml、Xpath 语法、正则表达式、CSV 和二进制数据储存、列表操作完整代码:https://github.com/TRHX/Python3-Spider-Practice/tree/master/douban-top250其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278","text":"爬取时间:2019-09-27爬取难度:★★☆☆☆☆请求链接:https://movie.douban.com/top250 以及每部电影详情页爬取目标:爬取榜单上每一部电影详情页的数据,保存为 CSV 文件;下载所有电影海报到本地涉及知识:请求库 requests、解析库 lxml、Xpath 语法、正则表达式、CSV 和二进制数据储存、列表操作完整代码:https://github.com/TRHX/Python3-Spider-Practice/tree/master/douban-top250其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278 【1x00】循环爬取网页模块观察豆瓣电影 Top 250,请求地址为:https://movie.douban.com/top250 每页展示25条电影信息,照例翻页观察 url 的变化: 第一页:https://movie.douban.com/top250 第二页:https://movie.douban.com/top250?start=25&filter= 第三页:https://movie.douban.com/top250?start=50&filter= 一共有10页,每次改变的是 start 的值,利用一个 for 循环,从 0 到 250 每隔 25 取一个值拼接到 url,实现循环爬取每一页,由于我们的目标是进入每一部电影的详情页,然后爬取详情页的内容,所以我们可以使用 Xpath 提取每一页每部电影详情页的 URL,将其赋值给 m_urls,并返回 m_urls,m_urls 是一个列表,列表元素就是电影详情页的 URL 12345678910def index_pages(number): url = 'https://movie.douban.com/top250?start=%s&filter=' % number index_response = requests.get(url=url, headers=headers) tree = etree.HTML(index_response.text) m_urls = tree.xpath(\"//li/div/div/a/@href\") return m_urlsif __name__ == '__main__': for i in range(0, 250, 25): movie_urls = index_pages(i) 【2x00】解析模块定义一个解析函数 parse_pages(),利用 for 循环,依次提取 index_pages() 函数返回的列表中的元素,也就是每部电影详情页的 URL,将其传给解析函数进行解析 1234567891011def index_pages(number): expressionsdef parse_pages(url): expressionsif __name__ == '__main__': for i in range(0, 250, 25): movie_urls = index_pages(i) for movie_url in movie_urls: results = parse_pages(movie_url) 详细看一下解析函数 parse_pages(),首先要对接收到的详情页 URL 发送请求,获取响应内容,然后再使用 Xpath 提取相关信息 123def parse_pages(url): movie_pages = requests.get(url=url, headers=headers) parse_movie = etree.HTML(movie_pages.text) 【2x01】Xpath 解析排名、电影名、评分信息其中排名、电影名和评分信息是最容易匹配到的,直接使用 Xpath 语法就可以轻松解决: 12345678# 排名ranking = parse_movie.xpath(\"//span[@class='top250-no']/text()\")# 电影名name = parse_movie.xpath(\"//h1/span[1]/text()\")# 评分score = parse_movie.xpath(\"//div[@class='rating_self clearfix']/strong/text()\") 【2x02】Xpath 解析参评人数接下来准备爬取有多少人参与了评价,分析一下页面: 如果只爬取这个 <span> 标签下的数字的话,没有任何提示信息,别人看了不知道是啥东西,所以把 人评价 这三个字也爬下来的话就比较好了,但是可以看到数字和文字不在同一个元素标签下,而且文字部分还有空格,要爬取的话就要把 class="rating_people" 的 a 标签下所有的 text 提取出来,然后再去掉空格: 123456789# 参评人数# 匹配a节点value = parse_movie.xpath(\"//a[@class='rating_people']\")# 提取a节点下所有文本string = [value[0].xpath('string(.)')]# 去除多余空格number = [a.strip() for a in string]# 此时 number = ['1617307人评价'] 这样做太麻烦了,我们可以直接提取数字,得到一个列表,然后使用另一个带有提示信息的列表,将两个列表的元素合并,组成一个新列表,这个新列表的元素就是提示信息+人数123456# 参评人数value = parse_movie.xpath(\"//span[@property='v:votes']/text()\")# 合并元素number = [\" \".join(['参评人数:'] + value)]# 此时 number = ['参评人数:1617307'] 【2x03】正则表达式解析制片国家、语言接下来尝试爬取制片国家/地区、语言等信息: 分析页面可以观察到,制片国家/地区和语言结构比较特殊,没有特别的 class 或者 id 属性,所包含的层次关系也太复杂,所以这里为了简便,直接采用正则表达式来匹配信息,就没有那么复杂了: 1234567# 制片国家/地区value = re.findall('<span class=\"pl\">制片国家/地区:</span>(.*?)<br/>', movie_pages.text)country = [\" \".join(['制片国家:'] + value)]# 语言value = re.findall('<span class=\"pl\">语言:</span>(.*?)<br/>', movie_pages.text)language = [\" \".join(['语言:'] + value)] 【3x00】返回解析数据其他剩下的信息皆可利用以上方法进行提取,所有信息提取完毕,最后使用 zip() 函数,将所有提取的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表 1return zip(ranking, name, score, number, types, country, language, date, time, other_name, director, screenwriter, performer, m_url, imdb_url) 【4x00】数据储存模块定义一个数据保存函数 save_results() 1234def save_results(data): with open('douban.csv', 'a', encoding=\"utf-8-sig\") as fp: writer = csv.writer(fp) writer.writerow(data) 注意:编码方式要设置为 utf-8-sig,如果设置为 utf-8,则文件会乱码,不设置编码,则可能会报一下类似错误: 1UnicodeEncodeError: 'gbk' codec can't encode character '\\ub3c4' in position 9: illegal multibyte sequence 可以看到错误出现在 \\ub3c4 上,将该 Unicode 编码转换为中文为 도,发现正是排名第 19 的电影:熔炉 도가니,因为标题有韩文,所以在储存为 CSV 文件时会报编码错误,而将编码设置为 utf-8-sig 就不会报错,具体原因参见:《Python 中文日文汉字乱码处理utf-8-sig》 接下来是保存电影的海报到本地: 1234567891011# 保存电影海报poster = parse_movie.xpath(\"//div[@id='mainpic']/a/img/@src\")response = requests.get(poster[0])name2 = re.sub(r'[A-Za-z\\:\\s]', '', name[0])poster_name = str(ranking[0]) + ' - ' + name2 + '.jpg'dir_name = 'douban_poster'if not os.path.exists(dir_name): os.mkdir(dir_name)poster_path = dir_name + '/' + poster_namewith open(poster_path, \"wb\")as f: f.write(response.content) 解析电影详情页,使用 Xpath 提取海报的 URL,向该 URL 发送请求 图片以 排名+电影名.jpg 的方式命名,但是由于提取的电影名部分含有特殊字符,比如排名第 10 的电影:忠犬八公的故事 Hachi: A Dog’s Tale,其中有个冒号,而 Windows 文件命名是不能包含这些字符的,所以我们直接去除电影名包含的英文字符、空白字符、特殊字符,只留下中文,代码实现: name2 = re.sub(r'[A-Za-z\\:\\s]', '', name[0]) 定义一个文件夹名称 douban_poster,利用 os 模块判断当前是否存在该文件夹,若不存在就创建一个 最后以二进制形式保存海报到当前目录的 douban_poster 文件夹下 【5x00】完整代码123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124# =============================================# --*-- coding: utf-8 --*--# @Time : 2019-09-27# @Author : TRHX# @Blog : www.itrhx.com# @CSDN : https://blog.csdn.net/qq_36759224# @FileName: douban.py# @Software: PyCharm# =============================================import requestsfrom lxml import etreeimport csvimport reimport timeimport osheaders = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'}def index_pages(number): url = 'https://movie.douban.com/top250?start=%s&filter=' % number index_response = requests.get(url=url, headers=headers) tree = etree.HTML(index_response.text) m_urls = tree.xpath(\"//li/div/div/a/@href\") return m_urlsdef parse_pages(url): movie_pages = requests.get(url=url, headers=headers) parse_movie = etree.HTML(movie_pages.text) # 排名 ranking = parse_movie.xpath(\"//span[@class='top250-no']/text()\") # 电影名 name = parse_movie.xpath(\"//h1/span[1]/text()\") # 评分 score = parse_movie.xpath(\"//div[@class='rating_self clearfix']/strong/text()\") # 参评人数 value = parse_movie.xpath(\"//span[@property='v:votes']/text()\") number = [\" \".join(['参评人数:'] + value)] # value = parse_movie.xpath(\"//a[@class='rating_people']\") # string = [value[0].xpath('string(.)')] # number = [a.strip() for a in string] # print(number) # 类型 value = parse_movie.xpath(\"//span[@property='v:genre']/text()\") types = [\" \".join(['类型:'] + value)] # 制片国家/地区 value = re.findall('<span class=\"pl\">制片国家/地区:</span>(.*?)<br/>', movie_pages.text) country = [\" \".join(['制片国家:'] + value)] # 语言 value = re.findall('<span class=\"pl\">语言:</span>(.*?)<br/>', movie_pages.text) language = [\" \".join(['语言:'] + value)] # 上映时期 value = parse_movie.xpath(\"//span[@property='v:initialReleaseDate']/text()\") date = [\" \".join(['上映日期:'] + value)] # 片长 value = parse_movie.xpath(\"//span[@property='v:runtime']/text()\") time = [\" \".join(['片长:'] + value)] # 又名 value = re.findall('<span class=\"pl\">又名:</span>(.*?)<br/>', movie_pages.text) other_name = [\" \".join(['又名:'] + value)] # 导演 value = parse_movie.xpath(\"//div[@id='info']/span[1]/span[@class='attrs']/a/text()\") director = [\" \".join(['导演:'] + value)] # 编剧 value = parse_movie.xpath(\"//div[@id='info']/span[2]/span[@class='attrs']/a/text()\") screenwriter = [\" \".join(['编剧:'] + value)] # 主演 value = parse_movie.xpath(\"//div[@id='info']/span[3]\") performer = [value[0].xpath('string(.)')] # URL m_url = ['豆瓣链接:' + movie_url] # IMDb链接 value = parse_movie.xpath(\"//div[@id='info']/a/@href\") imdb_url = [\" \".join(['IMDb链接:'] + value)] # 保存电影海报 poster = parse_movie.xpath(\"//div[@id='mainpic']/a/img/@src\") response = requests.get(poster[0]) name2 = re.sub(r'[A-Za-z\\:\\s]', '', name[0]) poster_name = str(ranking[0]) + ' - ' + name2 + '.jpg' dir_name = 'douban_poster' if not os.path.exists(dir_name): os.mkdir(dir_name) poster_path = dir_name + '/' + poster_name with open(poster_path, \"wb\")as f: f.write(response.content) return zip(ranking, name, score, number, types, country, language, date, time, other_name, director, screenwriter, performer, m_url, imdb_url)def save_results(data): with open('douban.csv', 'a', encoding=\"utf-8-sig\") as fp: writer = csv.writer(fp) writer.writerow(data)if __name__ == '__main__': num = 0 for i in range(0, 250, 25): movie_urls = index_pages(i) for movie_url in movie_urls: results = parse_pages(movie_url) for result in results: num += 1 save_results(result) print('第' + str(num) + '条电影信息保存完毕!') time.sleep(3) 【6x00】数据截图 【7x00】程序不足的地方程序不足的地方:豆瓣电影有反爬机制,当程序爬取到大约 150 条数据的时候,IP 就会被封掉,第二天 IP 才会解封,可以考虑综合使用多个代理、多个 User-Agent、随机时间暂停等方法进行爬取","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫实战","slug":"Python3-学习笔记/爬虫实战","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫实战/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"豆瓣电影","slug":"豆瓣电影","permalink":"https://www.itrhx.com/tags/豆瓣电影/"}]},{"title":"Python3 爬虫实战 — 猫眼电影TOP100","slug":"A51-pyspider-maoyantop100","date":"2019-09-24T11:31:56.965Z","updated":"2019-10-21T04:00:20.669Z","comments":true,"path":"2019/09/24/A51-pyspider-maoyantop100/","link":"","permalink":"https://www.itrhx.com/2019/09/24/A51-pyspider-maoyantop100/","excerpt":"爬取时间:2019-09-23爬取难度:★☆☆☆☆☆请求链接:https://maoyan.com/board/4爬取目标:猫眼 TOP100 的电影名称、排名、主演、上映时间、评分、封面图地址,数据保存为 CSV 文件涉及知识:请求库 requests、解析库 lxml、Xpath 语法、CSV 文件储存完整代码:https://github.com/TRHX/Python3-Spider-Practice/tree/master/maoyan-top100其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278","text":"爬取时间:2019-09-23爬取难度:★☆☆☆☆☆请求链接:https://maoyan.com/board/4爬取目标:猫眼 TOP100 的电影名称、排名、主演、上映时间、评分、封面图地址,数据保存为 CSV 文件涉及知识:请求库 requests、解析库 lxml、Xpath 语法、CSV 文件储存完整代码:https://github.com/TRHX/Python3-Spider-Practice/tree/master/maoyan-top100其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278 【1x00】循环爬取网页模块观察猫眼电影TOP100榜,请求地址为:https://maoyan.com/board/4 每页展示10条电影信息,翻页观察 url 变化: 第一页:https://maoyan.com/board/4 第二页:https://maoyan.com/board/4?offset=10 第三页:https://maoyan.com/board/4?offset=20 一共有10页,利用一个 for 循环,从 0 到 100 每隔 10 取一个值拼接到 url,实现循环爬取每一页 12345678def index_page(number): url = 'https://maoyan.com/board/4?offset=%s' % number response = requests.get(url=url, headers=headers) return response.textif __name__ == '__main__': for i in range(0, 100, 10): index = index_page(i) 【2x00】解析模块定义一个页面解析函数 parse_page(),使用 lxml 解析库的 Xpath 方法依次提取电影排名(ranking)、电影名称(movie_name)、主演(performer)、上映时间(releasetime)、评分(score)、电影封面图 url(movie_img) 通过对主演部分的提取发现有多余的空格符和换行符,循环 performer 列表,使用 strip() 方法去除字符串头尾空格和换行符 电影评分分为整数部分和小数部分,依次提取两部分,循环遍历组成一个完整的评分 最后使用 zip() 函数,将所有提取的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表 123456789101112131415161718def parse_page(content): tree = etree.HTML(content) # 电影排名 ranking = tree.xpath(\"//dd/i/text()\") # 电影名称 movie_name = tree.xpath('//p[@class=\"name\"]/a/text()') # 主演 performer = tree.xpath(\"//p[@class='star']/text()\") performer = [p.strip() for p in performer] # 上映时间 releasetime = tree.xpath('//p[@class=\"releasetime\"]/text()') # 评分 score1 = tree.xpath('//p[@class=\"score\"]/i[@class=\"integer\"]/text()') score2 = tree.xpath('//p[@class=\"score\"]/i[@class=\"fraction\"]/text()') score = [score1[i] + score2[i] for i in range(min(len(score1), len(score2)))] # 电影封面图 movie_img = tree.xpath('//img[@class=\"board-img\"]/@data-src') return zip(ranking, movie_name, performer, releasetime, score, movie_img) 【3x00】数据储存模块定义一个 save_results() 函数,将所有数据保存到 maoyan.csv 文件 1234def save_results(result): with open('maoyan.csv', 'a') as fp: writer = csv.writer(fp) writer.writerow(result) 【4x00】完整代码1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859# =============================================# --*-- coding: utf-8 --*--# @Time : 2019-09-23# @Author : TRHX# @Blog : www.itrhx.com# @CSDN : https://blog.csdn.net/qq_36759224# @FileName: maoyan.py# @Software: PyCharm# =============================================import requestsfrom lxml import etreeimport csvheaders = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'}def index_page(number): url = 'https://maoyan.com/board/4?offset=%s' % number response = requests.get(url=url, headers=headers) return response.textdef parse_page(content): tree = etree.HTML(content) # 电影排名 ranking = tree.xpath(\"//dd/i/text()\") # 电影名称 movie_name = tree.xpath('//p[@class=\"name\"]/a/text()') # 主演 performer = tree.xpath(\"//p[@class='star']/text()\") performer = [p.strip() for p in performer] # 上映时间 releasetime = tree.xpath('//p[@class=\"releasetime\"]/text()') # 评分 score1 = tree.xpath('//p[@class=\"score\"]/i[@class=\"integer\"]/text()') score2 = tree.xpath('//p[@class=\"score\"]/i[@class=\"fraction\"]/text()') score = [score1[i] + score2[i] for i in range(min(len(score1), len(score2)))] # 电影封面图 movie_img = tree.xpath('//img[@class=\"board-img\"]/@data-src') return zip(ranking, movie_name, performer, releasetime, score, movie_img)def save_results(result): with open('maoyan.csv', 'a') as fp: writer = csv.writer(fp) writer.writerow(result)if __name__ == '__main__': print('开始爬取数据...') for i in range(0, 100, 10): index = index_page(i) results = parse_page(index) for i in results: save_results(i) print('数据爬取完毕!') 【4x00】数据截图","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫实战","slug":"Python3-学习笔记/爬虫实战","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫实战/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"猫眼电影","slug":"猫眼电影","permalink":"https://www.itrhx.com/tags/猫眼电影/"}]},{"title":"Python3 爬虫学习笔记 C18","slug":"A50-Python3-spider-C18","date":"2019-09-21T03:59:30.358Z","updated":"2019-09-24T12:41:19.337Z","comments":true,"path":"2019/09/21/A50-Python3-spider-C18/","link":"","permalink":"https://www.itrhx.com/2019/09/21/A50-Python3-spider-C18/","excerpt":"Python3 爬虫学习笔记第十八章 —— 【爬虫框架 pyspider — 深入理解】","text":"Python3 爬虫学习笔记第十八章 —— 【爬虫框架 pyspider — 深入理解】 【18.1】启动参数常用启动命令:pyspider all,完整命令结构为:pyspider [OPTIONS] COMMAND [ARGS],OPTIONS 为可选参数,包含以下参数: -c, –config FILENAME:指定配置文件名称 –logging-config TEXT:日志配置文件名称,默认: pyspider/pyspider/logging.conf –debug:开启调试模式 –queue-maxsize INTEGER:队列的最大长度 –taskdb TEXT:taskdb 的数据库连接字符串,默认: sqlite –projectdb TEXT:projectdb 的数据库连接字符串,默认: sqlite –resultdb TEXT:resultdb 的数据库连接字符串,默认: sqlite –message-queue TEXT:消息队列连接字符串,默认: multiprocessing.Queue –phantomjs-proxy TEXT:PhantomJS 使用的代理,ip:port 的形式 –data-path TEXT:数据库存放的路径 –add-sys-path / –not-add-sys-path:将当前工作目录添加到python lib搜索路径 –version:显示 pyspider 的版本信息 –help:显示帮助信息 配置文件为一个 JSON 文件,一般为 config.json 文件,常用配置如下: 123456789101112{ \"taskdb\": \"mysql+taskdb://username:password@host:port/taskdb\", \"projectdb\": \"mysql+projectdb://username:password@host:port/projectdb\", \"resultdb\": \"mysql+resultdb://username:password@host:port/resultdb\", \"message_queue\": \"amqp://username:password@host:port/%2F\", \"webui\": { \"port\": 5000, \"username\": \"some_name\", \"password\": \"some_passwd\", \"need-auth\": true }} 可以设置对应的用户名,密码,端口等信息,使用命令 pyspider -c config.json all 即可运行 【18.2】运行单个组件pyspider 的架构主要分为 Scheduler(调度器)、Fetcher(抓取器)、Processer(处理器)三个部分,都可以单独运行,基本命令: pyspider [component_name] [options] 【18.2.1】运行 Scheduler1pyspider scheduler [OPTIONS] 123456789101112Options: --xmlrpc /--no-xmlrpc --xmlrpc-host TEXT --xmlrpc-port INTEGER --inqueue-limit INTEGER 任务队列的最大长度,如果满了则新的任务会被忽略 --delete-time INTEGER 设置为 delete 标记之前的删除时间 --active-tasks INTEGER 当前活跃任务数量配置 --loop-limit INTEGER 单轮最多调度的任务数量 --fail-pause-num INTEGER 上次失败时自动暂停项目暂停次数,任务失败,将0设置为禁用 --scheduler-cls TEXT Scheduler 使用的类 --threads TEXT ThreadBaseScheduler 的线程号,默认值:4 --help 显示帮助信息 【18.2.2】运行 Fetcher1pyspider fetcher [OPTIONS] 123456789101112Options: --xmlrpc /--no-xmlrpc --xmlrpc-host TEXT --xmlrpc-port INTEGER --poolsize INTEGER 同时请求的个数 --proxy TEXT 使用的代理 --user-agent TEXT 使用的 User-Agent --timeout TEXT 超时时间 --phantomjs-endpoint TEXT phantomjs 的端点,通过 pyspider 启动 phantomjs --splash-endpoint TEXT 执行 splash 的端点:http://splash.readthedocs.io/en/stable/api.html execut --fetcher-cls TEXT Fetcher 使用的类 --help 显示帮助信息 【18.2.3】运行 Processer1pyspider processor [OPTIONS] 1234Options: --processor-cls TEXT Processor 使用的类 --process-time-limit INTEGER 脚本处理时间限制 --help 显示帮助信息 【18.2.4】运行 WebUI1pyspider webui [OPTIONS] 1234567891011121314Options: --host TEXT 运行地址 --port INTEGER 运行端口 --cdn TEXT JS 和 CSS 的 CDN 服务器 --scheduler-rpc TEXT Scheduler 的 xmlrpc 路径 --fetcher-rpc TEXT Fetcher 的 xmlrpc 路径 --max-rate FLOAT 每个项目最大的 rate 值 --max-burst FLOAT 每个项目最大的 burst 值 --username TEXT Auth 验证的用户名 --password TEXT Auth 验证的密码 --need-auth 是否需要验证 --webui-instance TEXT 运行时使用的 Flask 应用 --process-time-limit INTEGER 调试中的脚本处理时间限制 --help 显示帮助信息 【18.3】crawl() 方法各参数参数文档:http://docs.pyspider.org/en/latest/apis/self.crawl/ url:爬取目标 URL,可以定义为单个 URL 字符串,也可以定义成 URL 列表 callback:回调函数,指定了该 URL 对应的响应内容用哪个方法来解析,示例: 12def on_start(self): self.crawl('http://www.itrhx.com/', callback=self.index_page) 代码解释:指定 callback 为 index_page,代表爬取 http://www.itrhx.com/ 得到的响应会用 index_page() 方法来解析,而 index_page() 方法的第一个参数就是响应对象,如下所示: 12def index_page(self, response): pass age:任务的有效时间,如果某个任务在有效时间内且已经被执行,则它不会重复执行,有如下两种设置方法: 12def on_start(self): self.crawl('http://www.itrhx.com/', callback=self.callback, age=10*24*60*60) 123@config(age=10 * 24 * 60 * 60)def callback(self): pass priority:爬取任务的优先级,其值默认是 0,priority 的数值越大,对应的请求会越优先被调度,如下所示,2.html 页面将会优先爬取: 123def index_page(self): self.crawl('http://www.itrhx.com/1.html', callback=self.index_page) self.crawl('http://www.itrhx.com/2.html', callback=self.detail_page, priority=1) exetime:设置定时任务,其值是时间戳,默认是 0,即代表立即执行,如下所示表示该任务会在 30 分钟之后执行: 123import timedef on_start(self): self.crawl('http://www.itrhx.com/', callback=self.callback, exetime=time.time()+30*60) retries:定义重试次数,其值默认是 3 itag:设置判定网页是否发生变化的节点值,在爬取时会判定次当前节点是否和上次爬取到的节点相同。如果节点相同,则证明页面没有更新,就不会重复爬取,如下所示: 123def index_page(self, response): for item in response.doc('.item').items(): self.crawl(item.find('a').attr.url, callback=self.detail_page, itag=item.find('.update-time').text()) 代码解释:设置 update-time 这个节点的值为 itag,在下次爬取时就会首先检测这个值有没有发生变化,如果没有变化,则不再重复爬取,否则执行爬取 auto_recrawl:开启时,爬取任务在过期后会重新执行,循环时间即定义的 age 时间长度,如下所示: 12def on_start(self): self.crawl('http://www.itrhx.com/', callback=self.callback, age=5*60*60, auto_recrawl=True) 代码解释:定义 age 有效期为 5 小时,设置了 auto_recrawl 为 True,这样任务就会每 5 小时执行一次 method:HTTP 请求方式,默认为 GET,如果想发起 POST 请求,可以将 method 设置为 POST params:定义 GET 请求参数,如下所示表示两个等价的爬取任务: 123def on_start(self): self.crawl('http://httpbin.org/get', callback=self.callback, params={'a': 123, 'b': 'c'}) self.crawl('http://httpbin.org/get?a=123&b=c', callback=self.callback) data:POST 表单数据,当请求方式为 POST 时,我们可以通过此参数传递表单数据,如下所示: 12def on_start(self): self.crawl('http://httpbin.org/post', callback=self.callback, method='POST', data={'a': 123, 'b': 'c'}) files:上传的文件,需要指定文件名,如下所示: 12def on_start(self): self.crawl('http://httpbin.org/post', callback=self.callback, method='POST', files={field: {filename: 'content'}}) user_agent:爬取使用的 User-Agent headers:爬取时使用的 Headers,即 Request Headers cookies:爬取时使用的 Cookies,为字典格式 connect_timeout:在初始化连接时的最长等待时间,默认为 20 秒 timeout:抓取网页时的最长等待时间,默认为 120 秒 allow_redirects:确定是否自动处理重定向,默认为 True validate_cert:确定是否验证证书,此选项对 HTTPS 请求有效,默认为 True proxy:爬取时使用的代理,支持用户名密码的配置,格式为 username:password@hostname:port,如下所示: 12def on_start(self): self.crawl('http://httpbin.org/get', callback=self.callback, proxy='127.0.0.1:9743') 也可以设置 craw_config 来实现全局配置,如下所示: 12class Handler(BaseHandler): crawl_config = {'proxy': '127.0.0.1:9743'} fetch_type:开启 PhantomJS 渲染,如果遇到 JavaScript 渲染的页面,指定此字段即可实现 PhantomJS 的对接,pyspider 将会使用 PhantomJS 进行网页的抓取,如下所示: 12def on_start(self): self.crawl('https://www.taobao.com', callback=self.index_page, fetch_type='js') js_script:页面加载完毕后执行的 JavaScript 脚本,如下所示,页面加载成功后将执行页面混动的 JavaScript 代码,页面会下拉到最底部: 1234567def on_start(self): self.crawl('http://www.example.org/', callback=self.callback, fetch_type='js', js_script=''' function() {window.scrollTo(0,document.body.scrollHeight); return 123; } ''') js_run_at:代表 JavaScript 脚本运行的位置,是在页面节点开头还是结尾,默认是结尾,即 document-end js_viewport_width/js_viewport_height:JavaScript 渲染页面时的窗口大小 load_images:在加载 JavaScript 页面时确定是否加载图片,默认为否 save:在不同的方法之间传递参数,如下所示: 123456def on_start(self): self.crawl('http://www.example.org/', callback=self.callback, save={'page': 1})def callback(self, response): return response.save['page'] cancel:取消任务,如果一个任务是 ACTIVE 状态的,则需要将 force_update 设置为 True force_update:即使任务处于 ACTIVE 状态,那也会强制更新状态 【18.4】任务区分pyspider 判断两个任务是否是重复的是使用的是该任务对应的 URL 的 MD5 值作为任务的唯一 ID,如果 ID 相同,那么两个任务就会判定为相同,其中一个就不会爬取了 某些情况下,请求的链接是同一个,但是 POST 的参数不同,这时可以重写 task_id() 方法,利用 URL 和 POST 的参数来生成 ID,改变这个 ID 的计算方式来实现不同任务的区分: 1234import jsonfrom pyspider.libs.utils import md5stringdef get_taskid(self, task): return md5string(task['url']+json.dumps(task['fetch'].get('data', ''))) 【18.5】全局配置pyspider 可以使用 crawl_config 来指定全局的配置,配置中的参数会和 crawl() 方法创建任务时的参数合并: 12345class Handler(BaseHandler): crawl_config = { 'headers': {'User-Agent': 'GoogleBot',} 'proxy': '127.0.0.1:9743' } 【18.6】定时爬取通过 every 属性来设置爬取的时间间隔,如下代码表示每天执行一次爬取: 1234@every(minutes=24 * 60)def on_start(self): for url in urllist: self.crawl(url, callback=self.index_page) 注意事项:如果设置了任务的有效时间(age 参数),因为在有效时间内爬取不会重复,所以要把有效时间设置得比重复时间更短,这样才可以实现定时爬取 错误举例:设定任务的过期时间为 5 天,而自动爬取的时间间隔为 1 天,当第二次尝试重新爬取的时候,pyspider 会监测到此任务尚未过期,便不会执行爬取: 1234567@every(minutes=24 * 60)def on_start(self): self.crawl('http://www.itrhx.com/', callback=self.index_page)@config(age=5 * 24 * 60 * 60)def index_page(self): pass","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫学习","slug":"Python3-学习笔记/爬虫学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫学习/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"pyspider","slug":"pyspider","permalink":"https://www.itrhx.com/tags/pyspider/"}]},{"title":"Python3 爬虫学习笔记 C17","slug":"A49-Python3-spider-C17","date":"2019-09-18T06:18:23.904Z","updated":"2019-09-24T12:41:15.652Z","comments":true,"path":"2019/09/18/A49-Python3-spider-C17/","link":"","permalink":"https://www.itrhx.com/2019/09/18/A49-Python3-spider-C17/","excerpt":"Python3 爬虫学习笔记第十七章 —— 【爬虫框架 pyspider — 基本使用】","text":"Python3 爬虫学习笔记第十七章 —— 【爬虫框架 pyspider — 基本使用】 【17.1】初识 pyspiderpyspider 是由国人 Binux 编写的一个 Python 爬虫框架 GitHub:https://github.com/binux/pyspider 官方文档(英文):http://docs.pyspider.org/ 非官方文档(中文):http://book.crifan.com/books/python_spider_pyspider/website/ 非官方文档(中文):https://www.cntofu.com/book/156/index.md pyspider 特性: python 脚本控制,可以使用任何 html 解析包(内置 pyquery) WEB 界面编写调试脚本,起停脚本,监控执行状态,查看活动历史,获取结果产出 支持 MySQL、MongoDB、Redis、SQLite、Elasticsearch、PostgreSQL 对接了 PhantomJS,支持抓取 JavaScript 的页面 组件可替换,支持单机和分布式部署,支持 Docker 部署 提供优先级控制、失败重试、定时抓取等功能 Windows 系统安装 pyspider: 使用命令 pip install pyspider 安装,若报 PyCurl 相关错误,可访问 https://www.lfd.uci.edu/~gohlke/pythonlibs/#pycurl 下载对应 wheel 文件并使用命令 pip install whl文件名 安装即可 如果要爬取 JavaScrip 渲染的页面,还要下载 PhantomJS,并将 PhantomJS 的路径配置到环境变量里,或者直接复制到 Python 安装目录的 Scripts 文件夹,需要用到数据库储存的话,同样要安装好相应的数据库 准备就绪后,使用 pyspider all 命令可启动 pyspider,浏览器打开:http://localhost:5000/ 可以看到 pyspider 的 WebUI 管理界面 【17.2】使用 pyspider 【17.2.1】主界面当成功创建了一个爬虫项目后,主界面如下所示: Recent Active Tasks:查看最近活动的任务,会跳转到一个页面有列表显示 Create:创建一个新的爬虫项目 group:定义项目的分组,以方便管理,若 group 设置为 delete,则该项目将会在24小时之后删除 project name:爬虫项目名称 status:项目状态,各状态如下: TODO:一个爬虫项目刚刚创建时的状态,此状态下可以编辑 Python 代码 STOP:中止项目的运行 CHECKING:当一个运行中的项目被编辑时项目状态会被自动设置成此状态并中止运行 DEBUG:会运行爬虫,顾名思义找 BUG,一般来说用于调试阶段 RUNNING:运行爬虫项目 PAUSED:项目暂停运行,默认没有这个状态,但是当你在运行过程中突然断网就会出现此状态 rate/burst:当前的爬取速率,rate 代表 1 秒发出多少个请求,burst 相当于流量控制中的令牌桶算法的令牌数,rate 和 burst 设置的越大,爬取速率越快,速率的设定需要考虑本机性能和爬取过快被封的问题 avg time:任务平均时间 process:5m、1h、1d 分别指的是最近 5 分、1 小时、1 天内的请求情况,all 代表所有的请求情况,请求由不同颜色表示,蓝色的代表等待被执行的请求,绿色的代表成功的请求,黄色的代表请求失败后等待重试的请求,红色的代表失败次数过多而被忽略的请求 actions:对爬虫项目的操作,各操作如下: Run:立即执行任务,需要 status 为 RUNNING 或者 DEBUG 状态;假如在配置的调度执行时间内已经执行过,再点 run 是无效的,需要删除 task.db 里的数据才行 Active Tasks:查看当前爬虫项目的活动任务 Results:查看项目运行结果 【17.2.2】项目界面创建一个爬虫项目,界面如下所示: 创建项目:点击 Create 即可新建一个爬虫项目 Project Name:爬虫项目名称 Start URL(s) :爬虫入口地址,选填,可在项目中更改 项目创建完成进入调试界面: 调试界面右边:编写代码的区域 调试界面左边:调试的区域,用于执行代码,显示输出信息等用途 run:单步调试爬虫程序,点击就可运行当前任务 < > 箭头:上一步、下一步,用于调试过程中切换到上一步骤或者下一步骤 save:保存当前代码,当代码变更后只有保存了再运行才能得到最新结果 enable css selector helper: CSS 选择器辅助程序 web:页面预览 html:可以查看页面源代码 follows:表示爬取请求,点击可查看所有的请求 在新建一个爬虫项目的时候,pyspider 已经自动生成了如下代码: 123456789101112131415161718192021222324252627#!/usr/bin/env python# -*- encoding: utf-8 -*-# Created on 2019-09-17 21:18:13# Project: 2from pyspider.libs.base_handler import *class Handler(BaseHandler): crawl_config = { } @every(minutes=24 * 60) def on_start(self): self.crawl('__START_URL__', callback=self.index_page) @config(age=10 * 24 * 60 * 60) def index_page(self, response): for each in response.doc('a[href^=\"http\"]').items(): self.crawl(each.attr.href, callback=self.detail_page) @config(priority=2) def detail_page(self, response): return { \"url\": response.url, \"title\": response.doc('title').text(), } class Handler():pyspider 爬虫的主类,可以在此处定义爬取、解析、存储的逻辑。整个爬虫的功能只需要一个 Handler 即可完成 crawl_config 属性:项目的所有爬取配置将会统一定义到这里,如定义 headers、设置代理等,配置之后全局生效 on_start() 方法:爬取入口,初始的爬取请求会在这里产生,该方法通过调用 crawl() 方法即可新建一个爬取请求,第一个参数是爬取的 URL,另一个参数 callback 指定了这个页面爬取成功后用哪个方法进行解析,默认指定为 index_page() 方法,即如果这个 URL 对应的页面爬取成功了,那 Response 将交给 index_page() 方法解析 index_page() 方法:接收 Response 参数,Response 对接了 pyquery。直接调用 doc() 方法传入相应的 CSS 选择器,就可以像 pyquery 一样解析此页面,代码中默认是 a[href^="http"],即解析页面的所有链接,然后将链接遍历,再次调用了 crawl() 方法生成了新的爬取请求,同时再指定了 callback 为 detail_page,表示这些页面爬取成功了就调用 detail_page() 方法解析。index_page() 实现了两个功能,一是将爬取的结果进行解析,二是生成新的爬取请求 detail_page() 方法:同样接收 Response 作为参数。detail_page() 抓取的就是详情页的信息,就不会生成新的请求,只对 Response 对象做解析,解析之后将结果以字典的形式返回。当然也可以进行后续处理,如将结果保存到数据库等操作 PS:pyspider 默认的 web 预览页面窗口较小,可以找到 pyspider 文件夹有个 debug.min.css 文件(如:E:\\Python\\Lib\\site-packages\\pyspider\\webui\\static\\debug.min.css),搜索 iframe,将原样式:iframe{border-width:0;width:100%} 改为 iframe{border-width:0;width:100%;height:400px !important} 即可,清除浏览器缓存后就会生效! 【17.3】使用 pyspider 爬取去哪儿网爬取地址:http://travel.qunar.com/travelbook/list.htm爬取目标:去哪儿网旅游攻略,发帖作者、标题、正文等 【17.3.1】爬取首页创建一个名为 qunar 的爬虫项目,Start URL 设置为 http://travel.qunar.com/travelbook/list.htm ,点击 run 出现一个爬取请求 左边调试区域出现以下代码: 12345678{ \"process\": { \"callback\": \"on_start\" }, \"project\": \"qunar\", \"taskid\": \"data:,on_start\", \"url\": \"data:,on_start\"} callback 为 on_start,表示此时执行了 on_start() 方法。在 on_start() 方法中,利用 crawl() 方法即可生成一个爬取请求,点击 index_page 链接后面的箭头会出现许多新的爬取请求,即首页所包含的所有链接 此时左边调试区域代码变为: 123456789101112{ \"fetch\": {}, \"process\": { \"callback\": \"index_page\" }, \"project\": \"qunar\", \"schedule\": { \"age\": 864000 }, \"taskid\": \"73a789f99528a2bdc3ab83a13902962a\", \"url\": \"http://travel.qunar.com/travelbook/list.htm\"} callback 变为了 index_page,表示此时执行了 index_page() 方法。传入 index_page() 方法的 response 参数为刚才生成的第一个爬取请求的 response 对象,然后调用 doc() 方法,传入提取所有 a 节点的 CSS 选择器,获取 a 节点的属性 href,实现了页面所有链接的提取,随后遍历所有链接,调用 crawl() 方法,把每个链接构造成新的爬取请求,可以看到 follows 新生成了 229 个爬取请求。点击 web 按钮可以直接预览当前页面,点击 html 按钮可以查看此页面源代码 【17.3.2】信息匹配代码 for each in response.doc('a[href^="http"]').items(): 实现了对整个页面链接的获取,我们需要提取网页的攻略的标题,内容等信息,那么直接替换 doc() 方法里的匹配语句即可,pyspider 提供了非常方便的 CSS 选择器,点击 enable css selector helper 按钮后,选择要匹配的信息并点击,再点击箭头 add to editor 即可得到匹配语句 完成了 CSS 选择器的替换,点击 save 保存,再次点击 run 重新执行 index_page() 方法,可以看到 follows 变为了 10 个,即抓取到了 10 篇攻略 【17.3.3】抓取下一页数据每一页只有 10 篇攻略,想要爬取所有页面的攻略,必须要得到下一页的数据,优化 index_page() 方法: 123456@config(age=10 * 24 * 60 * 60)def index_page(self, response): for each in response.doc('li > .tit > a').items(): self.crawl(each.attr.href, callback=self.detail_page) next = response.doc('.next').attr.href self.crawl(next, callback=self.index_page) 匹配下一页按钮,获取下一页按钮的 URL 并赋值给 next,将该 URL 传给 crawl() 方法,指定回调函数为 index_page() 方法,这样会再次调用 index_page() 方法,提取下一页的攻略标题 【17.3.4】抓取JS渲染数据随便点击一个获取到的攻略,预览该页面,可以观察到头图一直在加载中,切换到 html 查看源代码页面,可以观察到没有 img 节点,那么此处就是后期经过 JavaScript 渲染后才出现的 针对 JavaScript 渲染页面,可以通过 PhantomJS 来实现,具体到 pyspider 中,只需要在 index_page() 的 crawl() 抓取方法中添加一个参数 fetch_type 即可: 123456@config(age=10 * 24 * 60 * 60)def index_page(self, response): for each in response.doc('li > .tit > a').items(): self.crawl(each.attr.href, callback=self.detail_page, fetch_type='js') next = response.doc('.next').attr.href self.crawl(next, callback=self.index_page) 保存之后再次运行即可看到正常页面 【17.3.5】抓取所有数据改写 detail_page() 方法,同样通过 CSS 选择器提取 URL、标题、日期、作者、正文、图片等信息: 1234567891011@config(priority=2)def detail_page(self, response): return { 'url': response.url, 'title': response.doc('#booktitle').text(), 'date': response.doc('.when .data').text(), 'day': response.doc('.howlong .data').text(), 'who': response.doc('.who .data').text(), 'text': response.doc('#b_panel_schedule').text(), 'image': response.doc('.cover_img').attr.src } 【17.3.6】启动爬虫项目该爬虫项目完整代码如下: 12345678910111213141516171819202122232425262728293031323334#!/usr/bin/env python# -*- encoding: utf-8 -*-# Created on 2019-09-18 09:48:29# Project: qunarfrom pyspider.libs.base_handler import *class Handler(BaseHandler): crawl_config = { } @every(minutes=24 * 60) def on_start(self): self.crawl('http://travel.qunar.com/travelbook/list.htm', callback=self.index_page) @config(age=10 * 24 * 60 * 60) def index_page(self, response): for each in response.doc('li > .tit > a').items(): self.crawl(each.attr.href, callback=self.detail_page, fetch_type='js') next = response.doc('.next').attr.href self.crawl(next, callback=self.index_page) @config(priority=2) def detail_page(self, response): return { 'url': response.url, 'title': response.doc('#booktitle').text(), 'date': response.doc('.when .data').text(), 'day': response.doc('.howlong .data').text(), 'who': response.doc('.who .data').text(), 'text': response.doc('#b_panel_schedule').text(), 'image': response.doc('.cover_img').attr.src } 保存代码后,回到主界面,将项目 status 修改为 RUNNING ,点击 actions 的 run 按钮即可启动爬虫 点击 Active Tasks,即可查看最近请求的详细状况: 点击 Results,即可查看所有的爬取结果: 另外,右上角还可以选择 JSON、CSV 格式","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫学习","slug":"Python3-学习笔记/爬虫学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫学习/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"pyspider","slug":"pyspider","permalink":"https://www.itrhx.com/tags/pyspider/"}]},{"title":"Hexo 博客提交百度、谷歌搜索引擎收录","slug":"A48-submit-search-engine-inclusion","date":"2019-09-17T07:59:46.143Z","updated":"2019-12-29T07:20:01.329Z","comments":true,"path":"2019/09/17/A48-submit-search-engine-inclusion/","link":"","permalink":"https://www.itrhx.com/2019/09/17/A48-submit-search-engine-inclusion/","excerpt":"","text":"● 写在前面(必看)网站在没有提交搜索引擎收录之前,直接搜索你网站的内容是搜不到的,只有提交搜索引擎之后,搜索引擎才能收录你的站点,通过爬虫抓取你网站的东西,对于 hexo 博客来说,如果你是部署在 GitHub Pages,那么你是无法被百度收录的,因为 GitHub 禁止了百度爬虫,最常见的解决办法是双线部署到 Coding Pages 和 GitHub Pages,因为百度爬虫可以爬取到 Coding 上的内容,从而实现百度收录,如果你的 hexo 博客还没有实现双线部署,请参考:《Hexo 双线部署到 Coding Pages 和 GitHub Pages 并实现全站 HPPTS》,另外百度收录的所需的时间较长,大约半个月左右才会看到效果! ● 查看网站是否被收录首先我们可以输入 site:域名 来查看域名是否被搜索引擎收录,如下图所示,表示没有收录: ● 百度资源平台添加网站访问百度搜索资源平台官网,注册或者登陆百度账号,依次选择【用户中心】-【站点管理】,添加你的网站,在添加站点时会让你选择协议头(http 或者 https),如果选择 https,它会验证你的站点,大约能在一天之内完成,我的网站已经实现了全站 https,因此选择了 https 协议,但是不知道为什么始终验证失败,实在是无解,只能选择 http 协议了,如果你的站点也实现了全站 https,也可以尝试一下 之后会让你验证网站所有权,提供三种验证方式: 文件验证:下载给定的文件,将其放到本地主题目录 source 文件夹,然后部署上去完成验证 HTML 标签验证:一般是给一个 meta 标签,放到首页 <head> 与 </head> 标签之间即可完成验证 CNAME 验证:个人觉得这种方法最简单,去域名 DNS 添加一个 CNAME 记录即可完成验证 ● 提交百度搜索百度提供了自动提交和手动提交两种方式,其中自动提交又分为主动推送、自动推送和 sitemap 三种方式,以下是官方给出的解释: 主动推送:最为快速的提交方式,推荐您将站点当天新产出链接立即通过此方式推送给百度,以保证新链接可以及时被百度收录 自动推送:是轻量级链接提交组件,将自动推送的 JS 代码放置在站点每一个页面源代码中,当页面被访问时,页面链接会自动推送给百度,有利于新页面更快被百度发现 sitemap:您可以定期将网站链接放到sitemap中,然后将sitemap提交给百度。百度会周期性的抓取检查您提交的sitemap,对其中的链接进行处理,但收录速度慢于主动推送 手动提交:如果您不想通过程序提交,那么可以采用此种方式,手动将链接提交给百度 四种提交方式对比: 方式 主动推送 自动推送 Sitemap 手动提交 速度 最快 —— —— —— 开发成本 高 低 中 不需开发 可提交量 低 高 高 低 是否建议提交历史连接 否 是 是 是 和其他提交方法是否有冲突 无 无 无 无 个人推荐同时使用主动推送和 sitemap 方式,下面将逐一介绍这四种提交方式的具体实现方法 ● 主动推送在博客根目录安装插件 npm install hexo-baidu-url-submit --save,然后在根目录 _config.yml 文件里写入以下配置: 12345baidu_url_submit: count: 1 # 提交最新的多少个链接 host: www.itrhx.com # 在百度站长平台中添加的域名 token: your_token # 秘钥 path: baidu_urls.txt # 文本文档的地址, 新链接会保存在此文本文档里 其中的 token 可以在【链接提交】-【自动提交】-【主动推送】下面看到,接口调用地址最后面 token=xxxxx 即为你的 token 同样是在根目录的 _config.yml 文件,大约第 17 行处,url 要改为在百度站长平台添加的域名,也就是你网站的首页地址: 1234# URLurl: https://www.itrhx.comroot: /permalink: :year/:month/:day/:title/ 最后,加入新的 deployer: 123456789# Deployment## Docs: https://hexo.io/docs/deployment.htmldeploy:- type: git repository: github: git@github.com:TRHX/TRHX.github.io.git # 这是原来的 github 配置 coding: git@git.dev.tencent.com:TRHX/TRHX.git # 这是原来的 coding 配置 branch: master- type: baidu_url_submitter # 这是新加的主动推送 最后执行 hexo g -d 部署一遍即可实现主动推送,推送成功的标志是:在执行部署命令最后会显示类似如下代码: 12{\"remain\":4999953,\"success\":47}INFO Deploy done: baidu_url_submitter 这表示有 47 个页面已经主动推送成功,remain 的意思是当天剩余的可推送 url 条数 主动推送相关原理介绍: 新链接的产生:hexo generate 会产生一个文本文件,里面包含最新的链接 新链接的提交:hexo deploy 会从上述文件中读取链接,提交至百度搜索引擎 该插件的 GitHub 地址:https://github.com/huiwang/hexo-baidu-url-submit ● 自动推送关于自动推送百度官网给出的解释是:自动推送是百度搜索资源平台为提高站点新增网页发现速度推出的工具,安装自动推送JS代码的网页,在页面被访问时,页面URL将立即被推送给百度 此时要注意,有些 hexo 主题集成了这项功能,比如 next 主题,在 themes\\next\\layout_scripts\\ 下有个 baidu_push.swig 文件,我们只需要把如下代码粘贴到该文件,然后在主题配置文件设置 baidu_push: true 即可 12345678910111213141516{% if theme.baidu_push %}<script>(function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName(\"script\")[0]; s.parentNode.insertBefore(bp, s);})();</script>{% endif %} 然而大部分主题是没有集成这项功能的,对于大部分主题来说,我们可以把以下代码粘贴到 head.ejs 文件的 <head> 与 </head> 标签之间即可,从而实现自动推送(比如我使用的是 Material X 主题,那么只需要把代码粘贴到 \\themes\\material-x\\layout\\_partial\\head.ejs 中即可) 1234567891011121314<script>(function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName(\"script\")[0]; s.parentNode.insertBefore(bp, s);})();</script> ● sitemap首先我们要使用以下命令生成一个网站地图: 12npm install hexo-generator-sitemap --save npm install hexo-generator-baidu-sitemap --save 这里也注意一下,将根目录的 _config.yml 文件,大约第 17 行处,url 改为在百度站长平台添加的域名,也就是你网站的首页地址: 1234# URLurl: https://www.itrhx.comroot: /permalink: :year/:month/:day/:title/ 然后使用命令 hexo g -d 将网站部署上去,然后访问 你的首页/sitemap.xml 或者 你的首页/baidusitemap.xml 就可以看到网站地图了 比如我的是:https://www.itrhx.com/baidusitemap.xml 或者 https://www.itrhx.com/sitemap.xml 其中 sitemap.xml 文件是搜索引擎通用的 sitemap 文件,baidusitemap.xml 是百度专用的 sitemap 文件 然后来到百度站长平台的 sitemap 提交页面,将你的 sitemap 地址提交即可,如果成功的话状态会显示为正常,初次提交要等几分钟,sitemap.xml 相比 baidusitemap.xml 来说等待时间也会更长,如果以后你博客有新的文章或其他页面,可以点击手动更新文件,更新一下新的 sitemap ● 手动提交手动提交不需要其他额外操作,直接把需要收录的页面的 url 提交即可,这种方法效率较低,更新较慢,不推荐使用 ● 提交谷歌搜索提交谷歌搜索引擎比较简单,在提交之前,我们依然可以使用 site:域名 查看网站是否被收录,我的网站搭建了有差不多一年了,之前也没提交过收录,不过谷歌爬虫的确是强大,即使没有提交过,现在也能看到有一百多条结果了: 接下来我们将网站提交谷歌搜索引擎搜索,进入谷歌站长平台,登录你的谷歌账号之后会让你验证网站所有权: 有两种验证方式,分别是网域和网址前缀,两种资源类型区别如下: 网址前缀资源 网域资源 说明 仅包含具有指定前缀(包括协议 http/https)的网址。如果希望资源匹配任何协议或子网域(http/https/www./m. 等),建议改为添加网域资源。 包括所有子网域(m、www 等)和多种协议(http、https、ftp)的网域级资源。 验证 多种类型 仅 DNS 记录验证 示例 资源 http://example.com/✔ http://example.com/dresses/1234X https://example.com/dresses/1234X http://www.example.com/dresses/1234 资源 example.com✔ http://example.com/dresses/1234✔ https://example.com/dresses/1234✔ http://www.example.com/dresses/1234✔ http://support.m.example.com/dresses/1234 由对比可知选择网域资源验证方式比较好,只需要一个域名就可以匹配到多种格式的 URL,之后会给你一个 TXT 的记录值,复制它到你域名 DNS 增加一个 TXT 记录,点击验证即可 提交谷歌收录比较简单,选择站点地图,将我们之前生成的 sitemap 提交就行了,过几分钟刷新一下看到成功字样表示提交成功!","categories":[{"name":"Hexo","slug":"Hexo","permalink":"https://www.itrhx.com/categories/Hexo/"}],"tags":[{"name":"Hexo","slug":"Hexo","permalink":"https://www.itrhx.com/tags/Hexo/"},{"name":"SEO","slug":"SEO","permalink":"https://www.itrhx.com/tags/SEO/"}]},{"title":"Hexo 双线部署到 Coding Pages 和 GitHub Pages 并实现全站 HTTPS","slug":"A47-hexo-deployed-to-github-and-coding","date":"2019-09-16T06:11:40.959Z","updated":"2020-04-06T12:04:00.052Z","comments":true,"path":"2019/09/16/A47-hexo-deployed-to-github-and-coding/","link":"","permalink":"https://www.itrhx.com/2019/09/16/A47-hexo-deployed-to-github-and-coding/","excerpt":"","text":"部署到 Coding Pages 的好处:国内访问速度更快,可以提交百度收录(GitHub 禁止了百度的爬取) 部署到 Coding Pages 的坏处:就今年来说,Coding 不太稳定,随时有宕机的可能,群里的朋友已经经历过几次了,不过相信以后会越来越稳定的 部署过程中常见的问题:无法实现全站 HTTPS,Coding 申请 SSL 证书失败,浏览器可能会提示不是安全链接 本文前提:你已经将 Hexo 成功部署到了 GitHub Pages,如果还没有,请参考:《使用Github Pages和Hexo搭建自己的独立博客【超级详细的小白教程】》 本文将全面讲述如何成功双线部署到 Coding Pages 和 GitHub Pages 并实现全站 HPPTS,同时解决一些常见的问题! 1.创建项目进入 Coding 官网,点击个人版登陆,没有账号就注册一个并登录,由于 Coding 已经被腾讯收购了,所以登录就会来到腾讯云开发者平台,点击创建项目 项目名称建议和你的用户名一致,这样做的好处是:到时候可以直接通过 user_name.coding.me 访问你的博客,如果项目名与用户名不一致,则需要通过 user_name.coding.me/project_name 才能访问,项目描述可以随便写 2.配置公钥配置 SSH 公钥方法与 GitHub Pages 的方式差不多,点击你的头像,依次选择【个人设置】-【SSH公钥】-【新增公钥】 前面部署到 GitHub Pages 的时候就已经有了一对公钥,我们直接将该公钥粘贴进去就行,公钥名称可以随便写,选中永久有效选项 PS:公钥储存位置一般在 C:\\Users\\用户名\\.ssh 目录下的 id_rsa.pub 文件里,用记事本打开复制其内容即可 添加公钥后,我们可以右键 Get Bash,输入以下命令来检查是否配置成功: 1ssh -T git@e.coding.net 若出现以下提示,则证明配置成功: 12Coding 提示: Hello XXX, You've connected to Coding.net via SSH. This is a personal key.XXX,你好,你已经通过 SSH 协议认证 Coding.net 服务,这是一个个人公钥 3.配置 _config.yml进入你的项目,在右下角有选择连接方式,选择 SSH 方式(HTTPS 方式也可以,但是这种方式有时候可能连接不上,SSH 连接不容易出问题),一键复制,然后打开你本地博客根目录的 _config.yml 文件,找到 deploy 关键字,添加 coding 地址:coding: git@git.dev.tencent.com:user_name/user_name.git,也就是刚刚复制的 SSH 地址。 【2020.04.06 更新】coding 地址格式现在有所改变,类似于 `git@e.coding.net:TRHX/TRHX.git`,记住去仓库复制你自己的即可。 添加完成后先执行命令 hexo clean 清理一下缓存,然后执行命令 hexo g -d 将博客双线部署到 Coding Pages 和 GitHub Pages,如下图所示表示部署成功: 4.开启 Coding Pages进入你的项目,在代码栏下选择 Pages 服务,一键开启 Coding Pages,等待几秒后刷新网页即可看到已经开启的 Coding Pages,到目前为止,你就可以通过 xxxx.coding.me(比如我的是 trhx.coding.me)访问你的 Coding Pages 页面了 【2020.04.06 更新】coding 分配的域名现在有所改变,类似于 https://p51l67.coding-pages.com 5.绑定域名并开启 HPPTS首先在你的域名 DNS 设置中添加一条 CNAME 记录指向 xxxx.coding.me,解析路线选择 默认,将 GitHub 的解析路线改为 境外,这样境外访问就会走 GitHub,境内就会走 Coding,也有人说阿里云是智能解析,自动分配路线,如果解析路线都是默认,境外访问同样会智能选择走 GitHub,境内走 Coding,我没有验证过,有兴趣的可以自己试试,我的解析如下图所示: 【2020.04.06 更新】coding 分配的域名现在有所改变,类似于 https://p51l67.coding-pages.com,请注意解析当中记录值的填写。 然后点击静态 Pages 应用右上角的设置,进入设置页面,这里要注意,如果你之前已经部署到了 GitHub Pages 并开启了 HTTPS,那么直接在设置页面绑定你自己的域名,SSL/TLS 安全证书就会显示申请错误,如下图所示,没有申请到 SSL 证书,当你访问你的网站时,浏览器就会提示不是安全连接 申请错误原因是:在验证域名所有权时会定位到 Github Pages 的主机上导致 SSL 证书申请失败 正确的做法是:先去域名 DNS 把 GitHub 的解析暂停掉,然后再重新申请 SSL 证书,大约十秒左右就能申请成功,然后开启强制 HTTPS 访问 这里也建议同时绑定有 www 前缀和没有 www 前缀的,如果要绑定没有 www 前缀的,首先要去域名 DNS 添加一个 A 记录,主机记录为 @,记录值为你博客 IP 地址,IP 地址可以在 cmd 命令行 ping 一下得到,然后在 Coding Pages 中设置其中一个为【首选】,另一个设置【跳转至首选】,这样不管用户是否输入 www 前缀都会跳到有 www 前缀的了 在博客资源引用的时候也要注意所有资源的 URL 必须是以 https:// 开头,不然浏览器依旧会提示不安全! 至此,我们的 Hexo 博客就成功双线部署到 Coding Pages 和 GitHub Pages 了,并且也实现了全站 HPPTS,最后来一张 GitHub Pages 和 Coding Pages 在国内的速度对比图,可以明显看到速度的提升","categories":[{"name":"Hexo","slug":"Hexo","permalink":"https://www.itrhx.com/categories/Hexo/"}],"tags":[{"name":"Hexo","slug":"Hexo","permalink":"https://www.itrhx.com/tags/Hexo/"},{"name":"Coding Pages","slug":"Coding-Pages","permalink":"https://www.itrhx.com/tags/Coding-Pages/"},{"name":"GitHub Pages","slug":"GitHub-Pages","permalink":"https://www.itrhx.com/tags/GitHub-Pages/"}]},{"title":"Python3 爬虫学习笔记 C16","slug":"A46-Python3-spider-C16","date":"2019-09-13T16:44:50.577Z","updated":"2019-09-24T12:43:19.863Z","comments":true,"path":"2019/09/14/A46-Python3-spider-C16/","link":"","permalink":"https://www.itrhx.com/2019/09/14/A46-Python3-spider-C16/","excerpt":"Python3 爬虫学习笔记第十六章 —— 【数据储存系列 — Redis】","text":"Python3 爬虫学习笔记第十六章 —— 【数据储存系列 — Redis】 【16.1】关于 RedisRedis 是一个基于内存的高效的键值型(key-value)非关系型数据库,它支持存储的 value 类型非常多,包括 string(字符串)、list(链表)、set(集合)、zset(sorted set –有序集合) 和 hash(哈希类型),它的性能十分优越,可以支持每秒十几万此的读/写操作,其性能远超数据库,并且还支持集群、分布式、主从同步等配置,原则上可以无限扩展,让更多的数据存储在内存中,此外,它还支持一定的事务能力,这保证了高并发的场景下数据的安全和一致性。 【16.2】使用 Redis首先安装 Redis 和 redis-py 库,管理 Redis 可以使用可视化工具 Redis Desktop Manager,该工具现在收费了,分享个 0.8.8.384 的免费版本 安装 redis-py 库:pip install redisRedis 官网:https://redis.io官方文档:https://redis.io/documentation中文官网:http://www.redis.cn中文教程:http://www.runoob.com/redis/redis-tutorial.htmlGitHub:https://github.com/antirez/redisRedis Windows下载地址一:https://github.com/microsoftarchive/redis/releases (最新版 3.2.100,似乎不再更新)Redis Windows下载地址二:https://github.com/tporadowski/redis/releases (最新版)Redis Desktop Manager 官网:https://redisdesktop.com/Redis Desktop Manager 0.8.8.384 免费版:https://pan.baidu.com/s/18MKeCqT0MG0hc89jfkpIkA (提取码:3ovc) 利用 Python 连接 Redis 示例: 12345from redis import StrictRedisredis = StrictRedis(host='localhost', port=6379, db=0, password='000000')redis.set('name', 'TRHX')print(redis.get('name')) 传入 Redis 的地址、运行端口、使用的数据库和密码, 4 个参数默认值分别为 localhost、6379、0 和 None,声明一个 StrictRedis 对象,调用 set() 方法,设置一个键值对,输出结果如下: 1b'TRHX' 另外也可以使用 ConnectionPool 来连接: 1234from redis import StrictRedis, ConnectionPool pool = ConnectionPool(host='localhost', port=6379, db=0, password='000000') redis = StrictRedis(connection_pool=pool) ConnectionPool 也支持通过 URL 来构建: 123redis://[:password]@host:port/db # 创建 Redis TCP 连接rediss://[:password]@host:port/db # 创建 Redis TCP+SSL 连接unix://[:password]@/path/to/socket.sock?db=db # 创建 Redis UNIX socket 连接 代码示例: 12345from redis import StrictRedis, ConnectionPoolurl = 'redis://:000000@localhost:6379/0' pool = ConnectionPool.from_url(url) redis = StrictRedis(connection_pool=pool) 以下是有关的键操作、字符串操作、列表操作、集合操作、散列操作的各种方法,记录一下,方便查阅来源:《Python3 网络爬虫开发实战(崔庆才著)》Redis 命令参考:http://redisdoc.com/ 、http://doc.redisfans.com/ 【16.3】Key(键)操作 方法 作用 参数说明 示例 示例说明 示例结果 exists(name) 判断一个键是否存在 name:键名 redis.exists(‘name’) 是否存在 name 这个键 True delete(name) 删除一个键 name:键名 redis.delete(‘name’) 删除 name 这个键 1 type(name) 判断键类型 name:键名 redis.type(‘name’) 判断 name 这个键类型 b’string’ keys(pattern) 获取所有符合规则的键 pattern:匹配规则 redis.keys(‘n*’) 获取所有以 n 开头的键 [b’name’] randomkey() 获取随机的一个键 randomkey() 获取随机的一个键 b’name’ rename(src, dst) 重命名键 src:原键名;dst:新键名 redis.rename(‘name’, ‘nickname’) 将 name 重命名为 nickname True dbsize() 获取当前数据库中键的数目 dbsize() 获取当前数据库中键的数目 100 expire(name, time) 设定键的过期时间,单位为秒 name:键名;time:秒数 redis.expire(‘name’, 2) 将 name 键的过期时间设置为 2 秒 True ttl(name) 获取键的过期时间,单位为秒,-1 表示永久不过期 name:键名 redis.ttl(‘name’) 获取 name 这个键的过期时间 -1 move(name, db) 将键移动到其他数据库 name:键名;db:数据库代号 move(‘name’, 2) 将 name 移动到 2 号数据库 True flushdb() 删除当前选择数据库中的所有键 flushdb() 删除当前选择数据库中的所有键 True flushall() 删除所有数据库中的所有键 flushall() 删除所有数据库中的所有键 True 【16.4】String(字符串)操作 方法 作用 参数说明 示例 示例说明 示例结果 set(name, value) 给数据库中键名为 name 的 string 赋予值 value name:键名;value:值 redis.set(‘name’, ‘Bob’) 给 name 这个键的 value 赋值为 Bob True get(name) 返回数据库中键名为 name 的 string 的 value name:键名 redis.get(‘name’) 返回 name 这个键的 value b’Bob’ getset(name, value) 给数据库中键名为 name 的 string 赋予值 value 并返回上次的 value name:键名;value:新值 redis.getset(‘name’, ‘Mike’) 赋值 name 为 Mike 并得到上次的 value b’Bob’ mget(keys, *args) 返回多个键对应的 value 组成的列表 keys:键名序列 redis.mget([‘name’, ‘nickname’]) 返回 name 和 nickname 的 value [b’Mike’, b’Miker’] setnx(name, value) 如果不存在这个键值对,则更新 value,否则不变 name:键名 redis.setnx(‘newname’, ‘James’) 如果 newname 这个键不存在,则设置值为 James 第一次运行结果是 True,第二次运行结果是 False setex(name, time, value) 设置可以对应的值为 string 类型的 value,并指定此键值对应的有效期 name:键名;time:有效期;value:值 redis.setex(‘name’, 1, ‘James’) 将 name 这个键的值设为 James,有效期为 1 秒 True setrange(name, offset, value) 设置指定键的 value 值的子字符串 name:键名;offset:偏移量;value:值 redis.set(‘name’, ‘Hello’) redis.setrange (‘name’, 6, ‘World’) 设置 name 为 Hello 字符串,并在 index 为 6 的位置补 World 11,修改后的字符串长度 mset(mapping) 批量赋值 mapping:字典或关键字参数 redis.mset({‘name1’: ‘Durant’, ‘name2’: ‘James’}) 将 name1 设为 Durant,name2 设为 James True msetnx(mapping) 键均不存在时才批量赋值 mapping:字典或关键字参数 redis.msetnx({‘name3’: ‘Smith’, ‘name4’: ‘Curry’}) 在 name3 和 name4 均不存在的情况下才设置二者值 True incr(name, amount=1) 键名为 name 的 value 增值操作,默认为 1,键不存在则被创建并设为 amount name:键名;amount:增长的值 redis.incr(‘age’, 1) age 对应的值增 1,若不存在,则会创建并设置为 1 1,即修改后的值 decr(name, amount=1) 键名为 name 的 value 减值操作,默认为 1,键不存在则被创建并将 value 设置为 - amount name:键名;amount:减少的值 redis.decr(‘age’, 1) age 对应的值减 1,若不存在,则会创建并设置为-1 -1,即修改后的值 append(key, value) 键名为 key 的 string 的值附加 value key:键名 redis.append(‘nickname’, ‘OK’) 向键名为 nickname 的值后追加 OK 13,即修改后的字符串长度 substr(name, start, end=-1) 返回键名为 name 的 string 的子字符串 name:键名;start:起始索引;end:终止索引,默认为-1,表示截取到末尾 redis.substr(‘name’, 1, 4) 返回键名为 name 的值的字符串,截取索引为 1~4 的字符 b’ello’ getrange(key, start, end) 获取键的 value 值从 start 到 end 的子字符串 key:键名;start:起始索引;end:终止索引 redis.getrange(‘name’, 1, 4) 返回键名为 name 的值的字符串,截取索引为 1~4 的字符 b’ello 【16.5】Hash(哈希表)操作 方法 作用 参数说明 示例 示例说明 示例结果 hset(name, key, value) 向键名为 name 的散列表中添加映射 name:键名;key:映射键名;value:映射键值 hset(‘price’, ‘cake’, 5) 向键名为 price 的散列表中添加映射关系,cake 的值为 5 1,即添加的映射个数 hsetnx(name, key, value) 如果映射键名不存在,则向键名为 name 的散列表中添加映射 name:键名;key:映射键名;value:映射键值 hsetnx(‘price’, ‘book’, 6) 向键名为 price 的散列表中添加映射关系,book 的值为 6 1,即添加的映射个数 hget(name, key) 返回键名为 name 的散列表中 key 对应的值 name:键名;key:映射键名 redis.hget(‘price’, ‘cake’) 获取键名为 price 的散列表中键名为 cake 的值 5 hmget(name, keys, *args) 返回键名为 name 的散列表中各个键对应的值 name:键名;keys:键名序列 redis.hmget(‘price’, [‘apple’, ‘orange’]) 获取键名为 price 的散列表中 apple 和 orange 的值 [b’3’, b’7’] hmset(name, mapping) 向键名为 name 的散列表中批量添加映射 name:键名;mapping:映射字典 redis.hmset(‘price’, {‘banana’: 2, ‘pear’: 6}) 向键名为 price 的散列表中批量添加映射 True hincrby(name, key, amount=1) 将键名为 name 的散列表中映射的值增加 amount name:键名;key:映射键名;amount:增长量 redis.hincrby(‘price’, ‘apple’, 3) key 为 price 的散列表中 apple 的值增加 3 6,修改后的值 hexists(name, key) 键名为 name 的散列表中是否存在键名为键的映射 name:键名;key:映射键名 redis.hexists(‘price’, ‘banana’) 键名为 price 的散列表中 banana 的值是否存在 True hdel(name, *keys) 在键名为 name 的散列表中,删除键名为键的映射 name:键名;keys:键名序列 redis.hdel(‘price’, ‘banana’) 从键名为 price 的散列表中删除键名为 banana 的映射 True hlen(name) 从键名为 name 的散列表中获取映射个数 name:键名 redis.hlen(‘price’) 从键名为 price 的散列表中获取映射个数 6 hkeys(name) 从键名为 name 的散列表中获取所有映射键名 name:键名 redis.hkeys(‘price’) 从键名为 price 的散列表中获取所有映射键名 [b’cake’, b’book’, b’banana’, b’pear’] hvals(name) 从键名为 name 的散列表中获取所有映射键值 name:键名 redis.hvals(‘price’) 从键名为 price 的散列表中获取所有映射键值 [b’5’, b’6’, b’2’, b’6’] hgetall(name) 从键名为 name 的散列表中获取所有映射键值对 name:键名 redis.hgetall(‘price’) 从键名为 price 的散列表中获取所有映射键值对 {b’cake’: b’5’, b’book’: b’6’, b’orange’: b’7’, b’pear’: b’6’} 【16.6】List(列表)操作 方法 作用 参数说明 示例 示例说明 示例结果 rpush(name, *values) 在键名为 name 的列表末尾添加值为 value 的元素,可以传多个 name:键名;values:值 redis.rpush(‘list’, 1, 2, 3) 向键名为 list 的列表尾添加 1、2、3 3,列表大小 lpush(name, *values) 在键名为 name 的列表头添加值为 value 的元素,可以传多个 name:键名;values:值 redis.lpush(‘list’, 0) 向键名为 list 的列表头部添加 0 4,列表大小 llen(name) 返回键名为 name 的列表的长度 name:键名 redis.llen(‘list’) 返回键名为 list 的列表的长度 4 lrange(name, start, end) 返回键名为 name 的列表中 start 至 end 之间的元素 name:键名;start:起始索引;end:终止索引 redis.lrange(‘list’, 1, 3) 返回起始索引为 1 终止索引为 3 的索引范围对应的列表 [b’3’, b’2’, b’1’] ltrim(name, start, end) 截取键名为 name 的列表,保留索引为 start 到 end 的内容 name:键名;start:起始索引;end:终止索引 ltrim(‘list’, 1, 3) 保留键名为 list 的索引为 1 到 3 的元素 True lindex(name, index) 返回键名为 name 的列表中 index 位置的元素 name:键名;index:索引 redis.lindex(‘list’, 1) 返回键名为 list 的列表索引为 1 的元素 b’2’ lset(name, index, value) 给键名为 name 的列表中 index 位置的元素赋值,越界则报错 name:键名;index:索引位置;value:值 redis.lset(‘list’, 1, 5) 将键名为 list 的列表中索引为 1 的位置赋值为 5 True lrem(name, count, value) 删除 count 个键的列表中值为 value 的元素 name:键名;count:删除个数;value:值 redis.lrem(‘list’, 2, 3) 将键名为 list 的列表删除两个 3 1,即删除的个数 lpop(name) 返回并删除键名为 name 的列表中的首元素 name:键名 redis.lpop(‘list’) 返回并删除名为 list 的列表中的第一个元素 b’5’ rpop(name) 返回并删除键名为 name 的列表中的尾元素 name:键名 redis.rpop(‘list’) 返回并删除名为 list 的列表中的最后一个元素 b’2’ blpop(keys, timeout=0) 返回并删除名称在 keys 中的 list 中的首个元素,如果列表为空,则会一直阻塞等待 keys:键名序列;timeout:超时等待时间,0 为一直等待 redis.blpop(‘list’) 返回并删除键名为 list 的列表中的第一个元素 [b’5’] brpop(keys, timeout=0) 返回并删除键名为 name 的列表中的尾元素,如果 list 为空,则会一直阻塞等待 keys:键名序列;timeout:超时等待时间,0 为一直等待 redis.brpop(‘list’) 返回并删除名为 list 的列表中的最后一个元素 [b’2’] rpoplpush(src, dst) 返回并删除名称为 src 的列表的尾元素,并将该元素添加到名称为 dst 的列表头部 src:源列表的键;dst:目标列表的 key redis.rpoplpush(‘list’, ‘list2’) 将键名为 list 的列表尾元素删除并将其添加到键名为 list2 的列表头部,然后返回 b’2’ 【16.7】Set(集合)操作 方法 作用 参数说明 示例 示例说明 示例结果 sadd(name, *values) 向键名为 name 的集合中添加元素 name:键名;values:值,可为多个 redis.sadd(‘tags’, ‘Book’, ‘Tea’, ‘Coffee’) 向键名为 tags 的集合中添加 Book、Tea 和 Coffee 这 3 个内容 3,即插入的数据个数 srem(name, *values) 从键名为 name 的集合中删除元素 name:键名;values:值,可为多个 redis.srem(‘tags’, ‘Book’) 从键名为 tags 的集合中删除 Book 1,即删除的数据个数 spop(name) 随机返回并删除键名为 name 的集合中的一个元素 name:键名 redis.spop(‘tags’) 从键名为 tags 的集合中随机删除并返回该元素 b’Tea’ smove(src, dst, value) 从 src 对应的集合中移除元素并将其添加到 dst 对应的集合中 src:源集合;dst:目标集合;value:元素值 redis.smove(‘tags’, ‘tags2’, ‘Coffee’) 从键名为 tags 的集合中删除元素 Coffee 并将其添加到键为 tags2 的集合 True scard(name) 返回键名为 name 的集合的元素个数 name:键名 redis.scard(‘tags’) 获取键名为 tags 的集合中的元素个数 3 sismember(name, value) 测试 member 是否是键名为 name 的集合的元素 name:键值 redis.sismember(‘tags’, ‘Book’) 判断 Book 是否是键名为 tags 的集合元素 True sinter(keys, *args) 返回所有给定键的集合的交集 keys:键名序列 redis.sinter([‘tags’, ‘tags2’]) 返回键名为 tags 的集合和键名为 tags2 的集合的交集 {b’Coffee’} sinterstore(dest, keys, *args) 求交集并将交集保存到 dest 的集合 dest:结果集合;keys:键名序列 redis.sinterstore (‘inttag’, [‘tags’, ‘tags2’]) 求键名为 tags 的集合和键名为 tags2 的集合的交集并将其保存为 inttag 1 sunion(keys, *args) 返回所有给定键的集合的并集 keys:键名序列 redis.sunion([‘tags’, ‘tags2’]) 返回键名为 tags 的集合和键名为 tags2 的集合的并集 {b’Coffee’, b’Book’, b’Pen’} sunionstore(dest, keys, *args) 求并集并将并集保存到 dest 的集合 dest:结果集合;keys:键名序列 redis.sunionstore (‘inttag’, [‘tags’, ‘tags2’]) 求键名为 tags 的集合和键名为 tags2 的集合的并集并将其保存为 inttag 3 sdiff(keys, *args) 返回所有给定键的集合的差集 keys:键名序列 redis.sdiff([‘tags’, ‘tags2’]) 返回键名为 tags 的集合和键名为 tags2 的集合的差集 {b’Book’, b’Pen’} sdiffstore(dest, keys, *args) 求差集并将差集保存到 dest 集合 dest:结果集合;keys:键名序列 redis.sdiffstore (‘inttag’, [‘tags’, ‘tags2’]) 求键名为 tags 的集合和键名为 tags2 的集合的差集并将其保存为 inttag 3 smembers(name) 返回键名为 name 的集合的所有元素 name:键名 redis.smembers(‘tags’) 返回键名为 tags 的集合的所有元素 {b’Pen’, b’Book’, b’Coffee’} srandmember(name) 随机返回键名为 name 的集合中的一个元素,但不删除元素 name:键值 redis.srandmember(‘tags’) 随机返回键名为 tags 的集合中的一个元素 Srandmember (name) 【16.8】SortedSet(有序集合)操作 方法 作用 参数说明 示例 示例说明 示例结果 zadd(name, args, *kwargs) 向键名为 name 的 zset 中添加元素 member,score 用于排序。如果该元素存在,则更新其顺序 name:键名;args:可变参数 redis.zadd(‘grade’, 100, ‘Bob’, 98, ‘Mike’) 向键名为 grade 的 zset 中添加 Bob(其 score 为 100),并添加 Mike(其 score 为 98) 2,即添加的元素个数 zrem(name, *values) 删除键名为 name 的 zset 中的元素 name:键名;values:元素 redis.zrem(‘grade’, ‘Mike’) 从键名为 grade 的 zset 中删除 Mike 1,即删除的元素个数 zincrby(name, value, amount=1) 如果在键名为 name 的 zset 中已经存在元素 value,则将该元素的 score 增加 amount;否则向该集合中添加该元素,其 score 的值为 amount name:键名;value:元素;amount:增长的 score 值 redis.zincrby(‘grade’, ‘Bob’, -2) 键名为 grade 的 zset 中 Bob 的 score 减 2 98.0,即修改后的值 zrank(name, value) 返回键名为 name 的 zset 中元素的排名,按 score 从小到大排序,即名次 name:键名;value:元素值 redis.zrank(‘grade’, ‘Amy’) 得到键名为 grade 的 zset 中 Amy 的排名 1 zrevrank(name, value) 返回键为 name 的 zset 中元素的倒数排名(按 score 从大到小排序),即名次 name:键名;value:元素值 redis.zrevrank (‘grade’, ‘Amy’) 得到键名为 grade 的 zset 中 Amy 的倒数排名 2 zrevrange(name, start, end, withscores= False) 返回键名为 name 的 zset(按 score 从大到小排序)中 index 从 start 到 end 的所有元素 name:键值;start:开始索引;end:结束索引;withscores:是否带 score redis.zrevrange (‘grade’, 0, 3) 返回键名为 grade 的 zset 中前四名元素 [b’Bob’, b’Mike’, b’Amy’, b’James’] zrangebyscore (name, min, max, start=None, num=None, withscores=False) 返回键名为 name 的 zset 中 score 在给定区间的元素 name:键名;min:最低 score;max:最高 score;start:起始索引;num:个数;withscores:是否带 score redis.zrangebyscore (‘grade’, 80, 95) 返回键名为 grade 的 zset 中 score 在 80 和 95 之间的元素 [b’Bob’, b’Mike’, b’Amy’, b’James’] zcount(name, min, max) 返回键名为 name 的 zset 中 score 在给定区间的数量 name:键名;min:最低 score;max:最高 score redis.zcount(‘grade’, 80, 95) 返回键名为 grade 的 zset 中 score 在 80 到 95 的元素个数 2 zcard(name) 返回键名为 name 的 zset 的元素个数 name:键名 redis.zcard(‘grade’) 获取键名为 grade 的 zset 中元素的个数 3 zremrangebyrank (name, min, max) 删除键名为 name 的 zset 中排名在给定区间的元素 name:键名;min:最低位次;max:最高位次 redis.zremrangebyrank (‘grade’, 0, 0) 删除键名为 grade 的 zset 中排名第一的元素 1,即删除的元素个数 zremrangebyscore (name, min, max) 删除键名为 name 的 zset 中 score 在给定区间的元素 name:键名;min:最低 score;max:最高 score redis.zremrangebyscore (‘grade’, 80, 90) 删除 score 在 80 到 90 之间的元素 1,即删除的元素个数 【16.9】RedisDumpRedisDump 是 Redis 一个数据导入导出工具,是基于 Ruby 实现的,首先访问 Ruby 官网安装对应操作系统的 Ruby:http://www.ruby-lang.org/zh_cn/downloads/ ,安装完成即可使用 gem 命令,该命令类似于 Python 当中的 pip 命令,使用 gem install redis-dump 即可完成 RedisDump 的安装,安装完成后就可以使用导出数据 redis-dump 命令和导入数据 redis-load 命令了 【16.9.1】导出数据 redis-dump在命令行输入 redis-dump -h 可以查看: 123456789101112Usage: E:/Ruby26-x64/bin/redis-dump [global options] COMMAND [command options] -u, --uri=S Redis URI (e.g. redis://hostname[:port]) -d, --database=S Redis database (e.g. -d 15) -a, --password=S Redis password (e.g. -a 'my@pass/word') -s, --sleep=S Sleep for S seconds after dumping (for debugging) -c, --count=S Chunk size (default: 10000) -f, --filter=S Filter selected keys (passed directly to redis' KEYS command) -b, --base64 Encode key values as base64 (useful for binary values) -O, --without_optimizations Disable run time optimizations -V, --version Display version -D, --debug --nosafe 命令解释: -u Redis 连接字符串 -d 数据库代号 -a 数据库密码 -s 导出之后的休眠时间 -c 分块大小,默认是 10000 -f 导出时的过滤器 -b 将键值编码为 base64(对二进制值有用) -O 禁用运行时优化 -V 显示版本 -D 开启调试 导出数据示例: 12345678910111213redis-dump# 指定端口redis-dump -u 127.0.0.1:6379# 指定端口和密码redis-dump -u :password@127.0.0.1:6379# 导出指定数据库redis-dump -u 127.0.0.1:6379 -d 3# 导出包含特定值的数据redis-dump -u 127.0.0.1:6379 -f age 输出示例: 1234567891011121314151617181920212223# 导出所有数据{\"db\":0,\"key\":\"name5\",\"ttl\":-1,\"type\":\"string\",\"value\":\"DDD\",\"size\":3}{\"db\":0,\"key\":\"name2\",\"ttl\":-1,\"type\":\"string\",\"value\":\"AAA\",\"size\":3}{\"db\":0,\"key\":\"name4\",\"ttl\":-1,\"type\":\"string\",\"value\":\"CCC\",\"size\":3}{\"db\":0,\"key\":\"name6\",\"ttl\":-1,\"type\":\"string\",\"value\":\"CCC\",\"size\":3}{\"db\":0,\"key\":\"name\",\"ttl\":-1,\"type\":\"string\",\"value\":\"TRHX\",\"size\":4}{\"db\":0,\"key\":\"name3\",\"ttl\":-1,\"type\":\"string\",\"value\":\"BBB\",\"size\":3}{\"db\":1,\"key\":\"name2\",\"ttl\":-1,\"type\":\"string\",\"value\":\"BBB\",\"size\":3}{\"db\":1,\"key\":\"name1\",\"ttl\":-1,\"type\":\"string\",\"value\":\"AAA\",\"size\":3}{\"db\":2,\"key\":\"name2\",\"ttl\":-1,\"type\":\"string\",\"value\":\"BBB\",\"size\":3}{\"db\":2,\"key\":\"name1\",\"ttl\":-1,\"type\":\"string\",\"value\":\"AAA\",\"size\":3}{\"db\":3,\"key\":\"name2\",\"ttl\":-1,\"type\":\"string\",\"value\":\"HHH\",\"size\":3}{\"db\":3,\"key\":\"name1\",\"ttl\":-1,\"type\":\"string\",\"value\":\"RRR\",\"size\":3}{\"db\":4,\"key\":\"age\",\"ttl\":-1,\"type\":\"string\",\"value\":\"20\",\"size\":2}{\"db\":4,\"key\":\"age2\",\"ttl\":-1,\"type\":\"string\",\"value\":\"19\",\"size\":2}# 导出 3 号数据库{\"db\":3,\"key\":\"name2\",\"ttl\":-1,\"type\":\"string\",\"value\":\"HHH\",\"size\":3}{\"db\":3,\"key\":\"name1\",\"ttl\":-1,\"type\":\"string\",\"value\":\"RRR\",\"size\":3}# 导出 key 包含 age 的数据{\"db\":4,\"key\":\"age\",\"ttl\":-1,\"type\":\"string\",\"value\":\"20\",\"size\":2}{\"db\":4,\"key\":\"age2\",\"ttl\":-1,\"type\":\"string\",\"value\":\"19\",\"size\":2} 导出所有数据为 JSON 文件: 1redis-dump -u 127.0.0.1:6379 > db_full.json 该命令将会在当前目录生成一个名为 db_full.json 的文件,文件内容如下: 1234567891011121314{\"db\":0,\"key\":\"name5\",\"ttl\":-1,\"type\":\"string\",\"value\":\"DDD\",\"size\":3}{\"db\":0,\"key\":\"name2\",\"ttl\":-1,\"type\":\"string\",\"value\":\"AAA\",\"size\":3}{\"db\":0,\"key\":\"name4\",\"ttl\":-1,\"type\":\"string\",\"value\":\"CCC\",\"size\":3}{\"db\":0,\"key\":\"name6\",\"ttl\":-1,\"type\":\"string\",\"value\":\"CCC\",\"size\":3}{\"db\":0,\"key\":\"name\",\"ttl\":-1,\"type\":\"string\",\"value\":\"TRHX\",\"size\":4}{\"db\":0,\"key\":\"name3\",\"ttl\":-1,\"type\":\"string\",\"value\":\"BBB\",\"size\":3}{\"db\":1,\"key\":\"name2\",\"ttl\":-1,\"type\":\"string\",\"value\":\"BBB\",\"size\":3}{\"db\":1,\"key\":\"name1\",\"ttl\":-1,\"type\":\"string\",\"value\":\"AAA\",\"size\":3}{\"db\":2,\"key\":\"name2\",\"ttl\":-1,\"type\":\"string\",\"value\":\"BBB\",\"size\":3}{\"db\":2,\"key\":\"name1\",\"ttl\":-1,\"type\":\"string\",\"value\":\"AAA\",\"size\":3}{\"db\":3,\"key\":\"name2\",\"ttl\":-1,\"type\":\"string\",\"value\":\"HHH\",\"size\":3}{\"db\":3,\"key\":\"name1\",\"ttl\":-1,\"type\":\"string\",\"value\":\"RRR\",\"size\":3}{\"db\":4,\"key\":\"age\",\"ttl\":-1,\"type\":\"string\",\"value\":\"20\",\"size\":2}{\"db\":4,\"key\":\"age2\",\"ttl\":-1,\"type\":\"string\",\"value\":\"19\",\"size\":2} 使用参数 -d 指定某个数据库的所有数据导出为 JSON 文件: 1redis-dump -u 127.0.0.1:6379 -d 4 > db_db4.json 该命令会将 4 号数据库的数据导出到 db_db4.json 文件: 12{\"db\":4,\"key\":\"age\",\"ttl\":-1,\"type\":\"string\",\"value\":\"20\",\"size\":2}{\"db\":4,\"key\":\"age2\",\"ttl\":-1,\"type\":\"string\",\"value\":\"19\",\"size\":2} 使用参数 -f 过滤数据,只导出特定的数据: 1redis-dump -u 127.0.0.1:6379 -f name > db_name.json 该命令会导出 key 包含 name 的数据到 db_name.json 文件: 123456789101112{\"db\":0,\"key\":\"name5\",\"ttl\":-1,\"type\":\"string\",\"value\":\"DDD\",\"size\":3}{\"db\":0,\"key\":\"name2\",\"ttl\":-1,\"type\":\"string\",\"value\":\"AAA\",\"size\":3}{\"db\":0,\"key\":\"name4\",\"ttl\":-1,\"type\":\"string\",\"value\":\"CCC\",\"size\":3}{\"db\":0,\"key\":\"name6\",\"ttl\":-1,\"type\":\"string\",\"value\":\"CCC\",\"size\":3}{\"db\":0,\"key\":\"name\",\"ttl\":-1,\"type\":\"string\",\"value\":\"TRHX\",\"size\":4}{\"db\":0,\"key\":\"name3\",\"ttl\":-1,\"type\":\"string\",\"value\":\"BBB\",\"size\":3}{\"db\":1,\"key\":\"name2\",\"ttl\":-1,\"type\":\"string\",\"value\":\"BBB\",\"size\":3}{\"db\":1,\"key\":\"name1\",\"ttl\":-1,\"type\":\"string\",\"value\":\"AAA\",\"size\":3}{\"db\":2,\"key\":\"name2\",\"ttl\":-1,\"type\":\"string\",\"value\":\"BBB\",\"size\":3}{\"db\":2,\"key\":\"name1\",\"ttl\":-1,\"type\":\"string\",\"value\":\"AAA\",\"size\":3}{\"db\":3,\"key\":\"name2\",\"ttl\":-1,\"type\":\"string\",\"value\":\"HHH\",\"size\":3}{\"db\":3,\"key\":\"name1\",\"ttl\":-1,\"type\":\"string\",\"value\":\"RRR\",\"size\":3} 【16.9.2】导入数据 redis-load在命令行输入 redis-load -h 可以查看: 123456789redis-load --help Try: redis-load [global options] COMMAND [command options] -u, --uri=S Redis URI (e.g. redis://hostname[:port]) -d, --database=S Redis database (e.g. -d 15) -s, --sleep=S Sleep for S seconds after dumping (for debugging) -n, --no_check_utf8 -V, --version Display version -D, --debug --nosafe 命令解释: -u Redis 连接字符串 -d 数据库代号,默认是全部 -s 导出之后的休眠时间 -n 不检测 UTF-8 编码 -V 显示版本 -D 开启调试 导入示例: 12345# 将 test.json 文件所有内容导入到数据库< test.json redis-load -u 127.0.0.1:6379# 将 test.json 文件 db 值为 6 的数据导入到数据库 < test.json redis-load -u 127.0.0.1:6379 -d 6 另外,以下方法也能导入数据: 12345# 将 test.json 文件所有内容导入到数据库cat test.json | redis-load -u 127.0.0.1:6379# 将 test.json 文件 db 值为 6 的数据导入到数据库 cat test.json | redis-load -u 127.0.0.1:6379 -d 6 注意:cat 是 Linux 系统专有的命令,在 Windows 系统里没有 cat 这个命令,可以使用 Windows 批处理命令 type 代替 cat","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫学习","slug":"Python3-学习笔记/爬虫学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫学习/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"Redis","slug":"Redis","permalink":"https://www.itrhx.com/tags/Redis/"}]},{"title":"Python3 爬虫学习笔记 C15","slug":"A45-Python3-spider-C15","date":"2019-09-10T11:46:13.293Z","updated":"2019-09-24T12:41:02.822Z","comments":true,"path":"2019/09/10/A45-Python3-spider-C15/","link":"","permalink":"https://www.itrhx.com/2019/09/10/A45-Python3-spider-C15/","excerpt":"Python3 爬虫学习笔记第十五章 —— 【代理的基本使用】","text":"Python3 爬虫学习笔记第十五章 —— 【代理的基本使用】 【15.1】代理初识大多数网站都有反爬虫机制,如果一段时间内同一个 IP 发送的请求过多,服务器就会拒绝访问,直接禁封该 IP,此时,设置代理即可解决这个问题,网络上有许多免费代理和付费代理,比如西刺代理,全网代理 IP,快代理等,设置代理需要用到的就是代理 IP 地址和端口号,如果电脑上装有代理软件(例如:酸酸乳SSR),软件一般会在本机创建 HTTP 或 SOCKS 代理服务,直接使用此代理也可以 【15.2】urllib 库使用代理1234567891011121314from urllib.error import URLErrorfrom urllib.request import ProxyHandler, build_openerproxy = '127.0.0.1:1080'proxy_handler = ProxyHandler({ 'http': 'http://' + proxy, 'https': 'https://' + proxy})opener = build_opener(proxy_handler)try: response = opener.open('http://httpbin.org/get') print(response.read().decode('utf8'))except URLError as e: print(e.reason) http://httpbin.org/get 是一个请求测试站点,借助 ProxyHandler 设置代理,参数为字典类型,键名为协议类型,键值为代理,代理的写法:proxy = '127.0.0.1:1080',其中 127.0.0.1 为 IP 地址,1080 为端口号,这里表示本机的代理软件已经在本地 1080 端口创建了代理服务,代理前面需要加上 http 或者 https 协议,当请求的链接为 http 协议时,ProxyHandler 会自动调用 http 代理,同理,当请求的链接为 https 协议时,ProxyHandler 会自动调用 https 代理,build_opener() 方法传入 ProxyHandler 对象来创建一个 opener,调用 open() 方法传入一个 url 即可通过代理访问该链接,运行结果为一个 JSON,origin 字段为此时客户端的 IP 12345678910{ \"args\": {}, \"headers\": { \"Accept-Encoding\": \"identity\", \"Host\": \"httpbin.org\", \"User-Agent\": \"Python-urllib/3.6\" }, \"origin\": \"168.70.60.141, 168.70.60.141\", \"url\": \"https://httpbin.org/get\"} 如果是需要认证的代理,只需要在代理前面加入代理认证的用户名密码即可: 1234567891011121314from urllib.error import URLErrorfrom urllib.request import ProxyHandler, build_openerproxy = 'username:password@127.0.0.1:1080'proxy_handler = ProxyHandler({ 'http': 'http://' + proxy, 'https': 'https://' + proxy})opener = build_opener(proxy_handler)try: response = opener.open('http://httpbin.org/get') print(response.read().decode('utf8'))except URLError as e: print(e.reason) 如果代理是 SOCKS5 类型,需要用到 socks 模块,设置代理方法如下: 扩展:SOCKS5 是一个代理协议,它在使用TCP/IP协议通讯的前端机器和服务器机器之间扮演一个中介角色,使得内部网中的前端机器变得能够访问 Internet 网中的服务器,或者使通讯更加安全 123456789101112import socksimport socketfrom urllib import requestfrom urllib.error import URLErrorsocks.set_default_proxy(socks.SOCKS5, '127.0.0.1', 1080)socket.socket = socks.socksockettry: response = request.urlopen('http://httpbin.org/get') print(response.read().decode('utf-8'))except URLError as e: print(e.reason) 【15.3】requests 库使用代理requests 库使用代理只需要传入 proxies 参数即可: 123456789101112import requestsproxy = '127.0.0.1:1080'proxies = ({ 'http': 'http://' + proxy, 'https': 'https://' + proxy})try: response = requests.get('http://httpbin.org/get', proxies=proxies) print(response.text)except requests.exceptions.ChunkedEncodingError as e: print('Error', e.args) 输出结果: 1234567891011{ \"args\": {}, \"headers\": { \"Accept\": \"*/*\", \"Accept-Encoding\": \"gzip, deflate\", \"Host\": \"httpbin.org\", \"User-Agent\": \"python-requests/2.22.0\" }, \"origin\": \"168.70.60.141, 168.70.60.141\", \"url\": \"https://httpbin.org/get\"} 同样的,如果是需要认证的代理,也只需要在代理前面加入代理认证的用户名密码即可: 123456789101112import requestsproxy = 'username:password@127.0.0.1:1080'proxies = ({ 'http': 'http://' + proxy, 'https': 'https://' + proxy})try: response = requests.get('http://httpbin.org/get', proxies=proxies) print(response.text)except requests.exceptions.ChunkedEncodingError as e: print('Error', e.args) 如果代理是 SOCKS5 类型,需要用到 requests[socks] 模块或者 socks 模块,使用 requests[socks] 模块时设置代理方法如下: 123456789101112import requestsproxy = '127.0.0.1:1080'proxies = { 'http': 'socks5://' + proxy, 'https': 'socks5://' + proxy}try: response = requests.get('http://httpbin.org/get', proxies=proxies) print(response.text)except requests.exceptions.ConnectionError as e: print('Error', e.args) 使用 socks 模块时设置代理方法如下(此类方法为全局设置): 1234567891011import requestsimport socksimport socketsocks.set_default_proxy(socks.SOCKS5, '127.0.0.1', 1080)socket.socket = socks.socksockettry: response = requests.get('http://httpbin.org/get') print(response.text)except requests.exceptions.ConnectionError as e: print('Error', e.args) 【15.4】Selenium 使用代理【15.4.1】Chrome12345678from selenium import webdriverproxy = '127.0.0.1:1080'chrome_options = webdriver.ChromeOptions()chrome_options.add_argument('--proxy-server=http://' + proxy)path = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe'browser = webdriver.Chrome(executable_path=path, chrome_options=chrome_options)browser.get('http://httpbin.org/get') 通过 ChromeOptions 来设置代理,在创建 Chrome 对象的时候用 chrome_options 参数传递即可,访问目标链接后显示如下信息: 12345678910111213{ \"args\": {}, \"headers\": { \"Accept\": \"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3\", \"Accept-Encoding\": \"gzip, deflate\", \"Accept-Language\": \"zh-CN,zh;q=0.9\", \"Host\": \"httpbin.org\", \"Upgrade-Insecure-Requests\": \"1\", \"User-Agent\": \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36\" }, \"origin\": \"168.70.60.141, 168.70.60.141\", \"url\": \"https://httpbin.org/get\"} 如果是认证代理,则设置方法如下: 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253from selenium import webdriverfrom selenium.webdriver.chrome.options import Optionsimport zipfileip = '127.0.0.1'port = 1080username = 'username'password = 'password'manifest_json = \"\"\"{\"version\":\"1.0.0\",\"manifest_version\": 2,\"name\":\"Chrome Proxy\",\"permissions\": [\"proxy\",\"tabs\",\"unlimitedStorage\",\"storage\",\"<all_urls>\",\"webRequest\",\"webRequestBlocking\"],\"background\": {\"scripts\": [\"background.js\"] }}\"\"\"background_js =\"\"\"var config = { mode: \"fixed_servers\", rules: { singleProxy: { scheme: \"http\", host: \"%(ip) s\", port: %(port) s } } }chrome.proxy.settings.set({value: config, scope: \"regular\"}, function() {});function callbackFn(details) { return { authCredentials: {username: \"%(username) s\", password: \"%(password) s\" } }}chrome.webRequest.onAuthRequired.addListener( callbackFn, {urls: [\"<all_urls>\"]}, ['blocking'])\"\"\" % {'ip': ip, 'port': port, 'username': username, 'password': password}plugin_file = 'proxy_auth_plugin.zip'with zipfile.ZipFile(plugin_file, 'w') as zp: zp.writestr(\"manifest.json\", manifest_json) zp.writestr(\"background.js\", background_js)chrome_options = Options()chrome_options.add_argument(\"--start-maximized\")path = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe'chrome_options.add_extension(plugin_file)browser = webdriver.Chrome(executable_path=path, chrome_options=chrome_options)browser.get('http://httpbin.org/get') 需要在本地创建一个 manifest.json 配置文件和 background.js 脚本来设置认证代理。运行代码之后本地会生成一个 proxy_auth_plugin.zip 文件来保存当前配置 【15.4.1】PhantomJS借助 service_args 参数,也就是命令行参数即可设置代理: 12345678910from selenium import webdriverservice_args = [ '--proxy=127.0.0.1:1080', '--proxy-type=http']path = r'F:\\PycharmProjects\\Python3爬虫\\phantomjs-2.1.1\\bin\\phantomjs.exe'browser = webdriver.PhantomJS(executable_path=path, service_args=service_args)browser.get('http://httpbin.org/get')print(browser.page_source) 运行结果: 12345678910111213<html><head></head><body><pre style=\"word-wrap: break-word; white-space: pre-wrap;\">{ \"args\": {}, \"headers\": { \"Accept\": \"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\", \"Accept-Encoding\": \"gzip, deflate\", \"Accept-Language\": \"zh-CN,en,*\", \"Host\": \"httpbin.org\", \"User-Agent\": \"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/538.1 (KHTML, like Gecko) PhantomJS/2.1.1 Safari/538.1\" }, \"origin\": \"168.70.60.141, 168.70.60.141\", \"url\": \"https://httpbin.org/get\"}</pre></body></html> 如果是需要认证的代理,只需要在 service_args 参数加入 –proxy-auth 选项即可: 1234567891011from selenium import webdriverservice_args = [ '--proxy=127.0.0.1:1080', '--proxy-type=http', '--proxy-auth=username:password']path = r'F:\\PycharmProjects\\Python3爬虫\\phantomjs-2.1.1\\bin\\phantomjs.exe'browser = webdriver.PhantomJS(executable_path=path, service_args=service_args)browser.get('http://httpbin.org/get')print(browser.page_source)","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫学习","slug":"Python3-学习笔记/爬虫学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫学习/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"代理","slug":"代理","permalink":"https://www.itrhx.com/tags/代理/"}]},{"title":"Python3 爬虫学习笔记 C14","slug":"A44-Python3-spider-C14","date":"2019-09-07T17:38:41.491Z","updated":"2019-09-24T12:43:31.445Z","comments":true,"path":"2019/09/08/A44-Python3-spider-C14/","link":"","permalink":"https://www.itrhx.com/2019/09/08/A44-Python3-spider-C14/","excerpt":"Python3 爬虫学习笔记第十四章 —— 【验证码对抗系列 — 点触验证码】","text":"Python3 爬虫学习笔记第十四章 —— 【验证码对抗系列 — 点触验证码】 【14.1】关于点触验证码点触验证码是由杭州微触科技有限公司研发的新一代的互联网验证码,使用点击的形式完成验证,采用专利的印刷算法以及加密算法,保证每次请求到的验证图具有极高的安全性,常见的点触验证码如下: 【14.2】点触验证码攻克思路点触验证码相对其他类型验证码比较复杂,如果依靠 OCR 图像识别点触验证码,则识别难度非常大,此时就要用到互联网的验证码服务平台,这些服务平台全部都是人工在线识别,准确率非常高,原理就是先将验证码图片提交给平台,平台会返回识别结果在图片中的坐标位置,然后我们再解析坐标模拟点击即可,常见的打码平台有超级鹰、云打码等,打码平台是收费的,拿超级鹰来说,1元 = 1000题分,识别一次验证码将花费一定的题分,不同类型验证码需要的题分不同,验证码越复杂所需题分越高,比如 7 位中文汉字需要 70 题分,常见 4 ~ 6 位英文数字只要 10 题分,其他打码平台价格也都差不多 以下以超级鹰打码平台和中国铁路12306官网来做练习 【14.3】模拟登录 12306 — 总体思路首先在超级鹰打码平台注册账号并申请一个软件 ID,官网:http://www.chaojiying.com/ ,先充值一块钱得到 1000 题分,观察 12306 官网,发现验证码是要我们点击所有满足条件的图片,一般有 1~4 张图片满足要求,由此可确定在超级鹰打码平台的验证码类型为 9004(坐标多选,返回1~4个坐标,如:x1,y1|x2,y2|x3,y3), 获取其 Python API:http://www.chaojiying.com/download/Chaojiying_Python.rar ,然后用 Selenium 模拟登陆,获取到验证码,并将验证码发送给超级鹰后台,返回识别图片的坐标,最后模拟点击即可,整个过程的实现由主程序 12306.py 和超级鹰 API chaojiying.py 组成 整个程序包含的函数: 12345678910def __init__(): 初始化 WebDriver、Chaojiying 对象等def crack(): 破解入口、获取、识别验证码、模拟登录def open(): 账号密码输入def get_screenshot(): 整个页面截图def get_touclick_element(): 获取验证码位置def get_position(): 获取验证码坐标def get_touclick_image(): 剪裁验证码部分def get_points(self, captcha_result): 分析超级鹰返回的坐标def touch_click_words(self, locations): 模拟点击符合要求的图片def login(self): 点击登陆按钮,完成模拟登录 整个程序用到的库: 1234567891011import timefrom io import BytesIOfrom PIL import Imagefrom selenium import webdriverfrom selenium.webdriver.chrome.options import Optionsfrom selenium.webdriver import ActionChainsfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECfrom chaojiying import Chaojiyingfrom selenium.common.exceptions import TimeoutException 【14.4】主函数123if __name__ == '__main__': crack = CrackTouClick() crack.crack() 【14.5】初始化函数1234567891011121314151617181920USERNAME = '155********'PASSWORD = '***********'CHAOJIYING_USERNAME = '*******'CHAOJIYING_PASSWORD = '*******'CHAOJIYING_SOFT_ID = '********'CHAOJIYING_KIND = '9004'class CrackTouClick(): def __init__(self): self.url = 'https://kyfw.12306.cn/otn/resources/login.html' path = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe' chrome_options = Options() chrome_options.add_argument('--start-maximized') self.browser = webdriver.Chrome(executable_path=path, chrome_options=chrome_options) self.wait = WebDriverWait(self.browser, 20) self.email = USERNAME self.password = PASSWORD self.chaojiying = Chaojiying_Client(CHAOJIYING_USERNAME, CHAOJIYING_PASSWORD, CHAOJIYING_SOFT_ID) 定义 12306 账号(USERNAME)、密码(PASSWORD)、超级鹰用户名(CHAOJIYING_USERNAME)、超级鹰登录密码(CHAOJIYING_PASSWORD)、超级鹰软件 ID(CHAOJIYING_SOFT_ID)、验证码类型(CHAOJIYING_KIND),登录链接 url:https://kyfw.12306.cn/otn/resources/login.html ,谷歌浏览器驱动的目录(path),浏览器启动参数,并将相关参数传递给超级鹰 API 【14.6】破解入口函数123456789101112131415161718def crack(self): self.open() image = self.get_touclick_image() bytes_array = BytesIO() image.save(bytes_array, format='PNG') result = self.chaojiying.PostPic(bytes_array.getvalue(), CHAOJIYING_KIND) print(result) locations = self.get_points(result) self.touch_click_words(locations) self.login() try: success = self.wait.until(EC.text_to_be_present_in_element((By.CSS_SELECTOR, '.welcome-name'), '用户姓名')) print(success) cc = self.browser.find_element(By.CSS_SELECTOR, '.welcome-name') print(cc.text) except TimeoutException: self.chaojiying.ReportError(result['pic_id']) self.crack() 调用 open() 函数输入账号密码 调用 get_touclick_image() 函数获取验证码图片 利用超级鹰 Python API PostPic() 方法即可把图片发送给超级鹰后台,发送的图像是字节流格式,返回的结果是一个 JSON,如果识别成功,典型的返回结果类似于:{'err_no': 0, 'err_str': 'OK', 'pic_id': '6002001380949200001', 'pic_str': '132,127|56,77', 'md5': '1f8e1d4bef8b11484cb1f1f34299865b'},其中,pic_str 就是识别的文字的坐标,是以字符串形式返回的,每个坐标都以 | 分隔 调用 get_points() 函数解析超级鹰识别结果 调用 touch_click_words() 函数对符合要求的图片进行点击,然后点击登陆按钮模拟登陆 使用 try-except 语句判断是否出现了用户信息,判断依据是是否有用户姓名的出现,出现的姓名和实际姓名一致则登录成功,如果失败了就重试,超级鹰会返回该分值 【14.7】账号密码输入函数123456789def open(self): self.browser.get(self.url) login = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.login-hd-account'))) login.click() time.sleep(3) username = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'input#J-userName'))) password = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'input#J-password'))) username.send_keys(self.email) password.send_keys(self.password) 分析页面可知,登陆页面 URL 为:https://kyfw.12306.cn/otn/resources/login.html ,该页面默认出现的是扫描二维码登陆,所以要先点击账号登录,找到该 CSS 元素为 login-hd-account,调用 click() 方法实现模拟点击,此时出现账号密码输入框,同样找到其 ID 分别为 J-userName 和 J-password,调用 send_keys() 方法输入账号密码 【14.8】页面截图函数1234def get_screenshot(self): screenshot = self.browser.get_screenshot_as_png() screenshot = Image.open(BytesIO(screenshot)) return screenshot 对整个页面进行截图 【14.9】验证码元素查找函数123def get_touclick_element(self): element = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.login-pwd-code'))) return element 同样分析页面,验证码所在位置的 CSS 为 login-pwd-code 【14.10】获取验证码坐标函数1234567def get_position(self): element = self.get_touclick_element() time.sleep(3) location = element.location size = element.size top, bottom, left, right = location['y'], location['y'] + size['height'], location['x'], location['x'] + size['width'] return (top, bottom, left, right) location 属性可以返回该图片对象在浏览器中的位置,坐标轴是以屏幕左上角为原点,x 轴向右递增,y 轴向下递增,size 属性可以返回该图片对象的高度和宽度,由此可以得到验证码的位置信息 【14.11】验证码剪裁函数123456def get_touclick_image(self, name='12306.png'): top, bottom, left, right = self.get_position() screenshot = self.get_screenshot() captcha = screenshot.crop((left, top, right, bottom)) captcha.save(name) return captcha 根据验证码的坐标信息,对页面截图进行剪裁,得到验证码部分,将其保存为 12306.png 【14.12】验证码坐标解析函数1234def get_points(self, captcha_result): groups = captcha_result.get('pic_str').split('|') locations = [[int(number) for number in group.split(',')] for group in groups] return locations get_points() 方法将超级鹰的验证码识别结果变成列表的形式 【14.13】验证码模拟点击函数1234def touch_click_words(self, locations): for location in locations: print(location) ActionChains(self.browser).move_to_element_with_offset(self.get_touclick_element(), location[0]/1.25, location[1]/1.25).click().perform() touch_click_words() 方法通过调用 move_to_element_with_offset() 方法依次传入解析后的坐标,点击即可 【14.14】模拟点击登陆函数123def login(self): submit = self.wait.until(EC.element_to_be_clickable((By.ID, 'J-login'))) submit.click() 分析页面,找到登陆按钮的 ID 为 J-login,调用 click() 方法模拟点击按钮实现登录 【14.15】效果实现动图最终实现效果图:(关键信息已经过打码处理) 【14.16】完整代码12306.py 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105import timefrom io import BytesIOfrom PIL import Imagefrom selenium import webdriverfrom selenium.webdriver.chrome.options import Optionsfrom selenium.webdriver import ActionChainsfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECfrom chaojiying import Chaojiying_Clientfrom selenium.common.exceptions import TimeoutExceptionUSERNAME = '155********'PASSWORD = '***********'CHAOJIYING_USERNAME = '***********'CHAOJIYING_PASSWORD = '***********'CHAOJIYING_SOFT_ID = '******'CHAOJIYING_KIND = '9004'class CrackTouClick(): def __init__(self): #登陆 self.url = 'https://kyfw.12306.cn/otn/resources/login.html' path = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe' chrome_options = Options() chrome_options.add_argument('--start-maximized') self.browser = webdriver.Chrome(executable_path=path, chrome_options=chrome_options) self.wait = WebDriverWait(self.browser, 20) self.email = USERNAME self.password = PASSWORD self.chaojiying = Chaojiying_Client(CHAOJIYING_USERNAME, CHAOJIYING_PASSWORD, CHAOJIYING_SOFT_ID) def crack(self): self.open() image = self.get_touclick_image() bytes_array = BytesIO() image.save(bytes_array, format='PNG') result = self.chaojiying.PostPic(bytes_array.getvalue(), CHAOJIYING_KIND) print(result) locations = self.get_points(result) self.touch_click_words(locations) self.login() try: success = self.wait.until(EC.text_to_be_present_in_element((By.CSS_SELECTOR, '.welcome-name'), '谭仁侯')) print(success) cc = self.browser.find_element(By.CSS_SELECTOR, '.welcome-name') print(cc.text) except TimeoutException: self.chaojiying.ReportError(result['pic_id']) self.crack() def open(self): self.browser.get(self.url) login = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.login-hd-account'))) login.click() time.sleep(3) username = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'input#J-userName'))) password = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'input#J-password'))) username.send_keys(self.email) password.send_keys(self.password) def get_screenshot(self): screenshot = self.browser.get_screenshot_as_png() screenshot = Image.open(BytesIO(screenshot)) return screenshot def get_touclick_element(self): element = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.login-pwd-code'))) return element def get_position(self): element = self.get_touclick_element() time.sleep(3) location = element.location size = element.size top, bottom, left, right = location['y'], location['y'] + size['height'], location['x'], location['x'] + size['width'] return (top, bottom, left, right) def get_touclick_image(self, name='12306.png'): top, bottom, left, right = self.get_position() screenshot = self.get_screenshot() captcha = screenshot.crop((left, top, right, bottom)) captcha.save(name) return captcha def get_points(self, captcha_result): groups = captcha_result.get('pic_str').split('|') locations = [[int(number) for number in group.split(',')] for group in groups] return locations def touch_click_words(self, locations): for location in locations: print(location) ActionChains(self.browser).move_to_element_with_offset(self.get_touclick_element(), location[0]/1.25, location[1]/1.25).click().perform() def login(self): submit = self.wait.until(EC.element_to_be_clickable((By.ID, 'J-login'))) submit.click()if __name__ == '__main__': crack = CrackTouClick() crack.crack() chaojiying.py 1234567891011121314151617181920212223242526272829303132333435363738394041424344import requestsfrom hashlib import md5class Chaojiying_Client(object): def __init__(self, username, password, soft_id): self.username = username password = password.encode('utf8') self.password = md5(password).hexdigest() self.soft_id = soft_id self.base_params = { 'user': self.username, 'pass2': self.password, 'softid': self.soft_id, } self.headers = { 'Connection': 'Keep-Alive', 'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)', } def PostPic(self, im, codetype): \"\"\" im: 图片字节 codetype: 题目类型 参考 http://www.chaojiying.com/price.html \"\"\" params = { 'codetype': codetype, } params.update(self.base_params) files = {'userfile': ('ccc.jpg', im)} r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, files=files, headers=self.headers) return r.json() def ReportError(self, im_id): \"\"\" im_id:报错题目的图片ID \"\"\" params = { 'id': im_id, } params.update(self.base_params) r = requests.post('http://upload.chaojiying.net/Upload/ReportError.php', data=params, headers=self.headers) return r.json()","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫学习","slug":"Python3-学习笔记/爬虫学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫学习/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"点触验证码","slug":"点触验证码","permalink":"https://www.itrhx.com/tags/点触验证码/"}]},{"title":"Python3 爬虫学习笔记 C13","slug":"A43-Python3-spider-C13","date":"2019-09-06T19:52:14.161Z","updated":"2019-09-24T12:40:56.234Z","comments":true,"path":"2019/09/07/A43-Python3-spider-C13/","link":"","permalink":"https://www.itrhx.com/2019/09/07/A43-Python3-spider-C13/","excerpt":"Python3 爬虫学习笔记第十三章 —— 【验证码对抗系列 — 滑动验证码】","text":"Python3 爬虫学习笔记第十三章 —— 【验证码对抗系列 — 滑动验证码】 【13.1】关于滑动验证码滑动验证码属于行为式验证码,需要通过用户的操作行为来完成验证,一般是根据提示用鼠标将滑块拖动到指定的位置完成验证,此类验证码背景图片采用多种图像加密技术,且添加了很多随机效果,能有效防止OCR文字识别,另外,验证码上的文字采用了随机印刷技术,能够随机采用多种字体、多种变形的实时随机印刷,防止暴力破解;斗鱼、哔哩哔哩、淘宝等平台都使用了滑动验证码 【13.2】滑动验证码攻克思路利用自动化测试工具 Selenium 直接模拟人的行为方式来完成验证,首先要分析页面,想办法找到滑动验证码的完整图片、带有缺口的图片和需要滑动的图片,通过对比原始的图片和带滑块缺口的图片的像素,像素不同的地方就是缺口位置,计算出滑块缺口的位置,得到所需要滑动的距离,最后利用 Selenium 进行对滑块的拖拽,拖拽时要模仿人的行为,由于有个对准过程,所以是先快后慢,匀速移动、随机速度移动都不会成功 以下以哔哩哔哩为例来做模拟登录练习 【13.3】模拟登录 bilibili — 总体思路首先使用 Selenium 模拟登陆 bilibili,自动输入账号密码,查找到登陆按钮并点击,使其出现滑动验证码,此时分析页面,滑动验证组件是由3个 canvas 组成,分别代表完整图片、带有缺口的图片和需要滑动的图片,3个 canvas 元素包含 CSS display 属性,display:block 为可见,display:none 为不可见,分别获取三张图片时要将其他两张图片设置为 display:none,获取元素位置后即可对图片截图并保存,通过图片像素对比,找到缺口位置即为滑块要移动的距离,随后构造滑动轨迹,按照先加速后减速的方式移动滑块完成验证。 整个程序包含的函数: 1234567891011def init(): 初始化函数,定义全局变量def login(): 登录函数,输入账号密码并点击登录def find_element(): 验证码元素查找函数,查找三张图的元素def hide_element(): 设置元素不可见函数def show_element(): 设置元素可见函数def save_screenshot(): 验证码截图函数,截取三张图并保存def slide(): 滑动函数def is_pixel_equal(): 像素判断函数,寻找缺口位置def get_distance(): 计算滑块移动距离函数def get_track(): 构造移动轨迹函数def move_to_gap(): 模拟拖动函数 整个程序用到的库: 12345678from selenium import webdriverfrom selenium.webdriver.chrome.options import Optionsfrom selenium.webdriver.support.wait import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECfrom selenium.webdriver.common.by import Byfrom selenium.webdriver import ActionChainsimport timeimport random 【13.4】主函数12345if __name__ == '__main__': init() login() find_element() slide() 【13.5】初始化函数12345678910def init(): global url, browser, username, password, wait url = 'https://passport.bilibili.com/login' path = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe' chrome_options = Options() chrome_options.add_argument('--start-maximized') browser = webdriver.Chrome(executable_path=path, chrome_options=chrome_options) username = '155********' password = '***********' wait = WebDriverWait(browser, 20) global 关键字定义了全局变量,随后是登录页面url、谷歌浏览器驱动的目录path、实例化 Chrome 浏览器、设置浏览器分辨率最大化、用户名、密码、WebDriverWait() 方法设置等待超时 【13.6】登录函数123456789def login(): browser.get(url) user = wait.until(EC.presence_of_element_located((By.ID, 'login-username'))) passwd = wait.until(EC.presence_of_element_located((By.ID, 'login-passwd'))) user.send_keys(username) passwd.send_keys(password) login_btn = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'a.btn.btn-login'))) time.sleep(random.random() * 3) login_btn.click() 等待用户名输入框和密码输入框对应的 ID 节点加载出来,分析页面可知,用户名输入框 id="login-username",密码输入框 id="login-passwd",获取这两个节点,调用 send_keys() 方法输入用户名和密码,随后获取登录按钮,分析页面可知登录按钮 class="btn btn-login",随机产生一个数并将其扩大三倍作为暂停时间,最后调用 click() 方法实现登录按钮的点击 【13.7】验证码元素查找函数12345678910111213def find_element(): c_background = wait.until( EC.presence_of_element_located((By.CSS_SELECTOR, 'canvas.geetest_canvas_bg.geetest_absolute'))) c_slice = wait.until( EC.presence_of_element_located((By.CSS_SELECTOR, 'canvas.geetest_canvas_slice.geetest_absolute'))) c_full_bg = wait.until( EC.presence_of_element_located((By.CSS_SELECTOR, 'canvas.geetest_canvas_fullbg.geetest_fade.geetest_absolute'))) hide_element(c_slice) save_screenshot(c_background, 'back') show_element(c_slice) save_screenshot(c_slice, 'slice') show_element(c_full_bg) save_screenshot(c_full_bg, 'full') 我们要获取验证码的三张图片,分别是完整的图片、带有缺口的图片和需要滑动的图片,分析页面代码,这三张图片是由 3 个 canvas 组成,3 个 canvas 元素包含 CSS display 属性,display:block 为可见,display:none 为不可见,在分别获取三张图片时要将其他两张图片设置为 display:none,这样做才能单独提取到每张图片,定位三张图片的 class 分别为:带有缺口的图片(c_background):geetest_canvas_bg geetest_absolute、需要滑动的图片(c_slice):geetest_canvas_slice geetest_absolute、完整图片(c_full_bg):geetest_canvas_fullbg geetest_fade geetest_absolute,随后传值给 save_screenshot() 函数,进一步对验证码进行处理 【13.8】元素可见性设置函数12345678# 设置元素不可见def hide_element(element): browser.execute_script("arguments[0].style=arguments[1]", element, "display: none;")# 设置元素可见def show_element(element): browser.execute_script("arguments[0].style=arguments[1]", element, "display: block;") 【13.9】验证码截图函数1234567891011121314151617181920def save_screenshot(obj, name): try: pic_url = browser.save_screenshot('.\\\\bilibili.png') print(\"%s:截图成功!\" % pic_url) left = obj.location['x'] top = obj.location['y'] right = left + obj.size['width'] bottom = top + obj.size['height'] print('图:' + name) print('Left %s' % left) print('Top %s' % top) print('Right %s' % right) print('Bottom %s' % bottom) print('') im = Image.open('.\\\\bilibili.png') im = im.crop((left, top, right, bottom)) file_name = 'bili_' + name + '.png' im.save(file_name) except BaseException as msg: print(\"%s:截图失败!\" % msg) location 属性可以返回该图片对象在浏览器中的位置,坐标轴是以屏幕左上角为原点,x轴向右递增,y轴向下递增,size 属性可以返回该图片对象的高度和宽度,由此可以得到验证码的位置信息,首先调用 save_screenshot() 属性对整个页面截图并保存,然后向 crop() 方法传入验证码的位置信息,由位置信息再对验证码进行剪裁并保存 【13.10】滑动函数123456def slide(): distance = get_distance(Image.open('.\\\\bili_back.png'), Image.open('.\\\\bili_full.png')) print('计算偏移量为:%s Px' % distance) trace = get_trace(distance - 5) move_to_gap(trace) time.sleep(3) 向 get_distance() 函数传入完整的图片和缺口图片,计算滑块需要滑动的距离,再把距离信息传入 get_trace() 函数,构造滑块的移动轨迹,最后根据轨迹信息调用 move_to_gap() 函数移动滑块完成验证 【13.11】计算滑块移动距离函数123456def get_distance(bg_image, fullbg_image): distance = 60 for i in range(distance, fullbg_image.size[0]): for j in range(fullbg_image.size[1]): if not is_pixel_equal(fullbg_image, bg_image, i, j): return i get_distance() 方法即获取缺口位置的方法,此方法的参数是两张图片,一张为完整的图片,另一张为带缺口的图片,distance 为滑块的初始位置,遍历两张图片的每个像素,利用 is_pixel_equal() 像素判断函数判断两张图片同一位置的像素是否相同,比较两张图 RGB 的绝对值是否均小于定义的阈值 threshold,如果绝对值均在阈值之内,则代表像素点相同,继续遍历,否则代表不相同的像素点,即缺口的位置 【13.12】像素判断函数123456789def is_pixel_equal(bg_image, fullbg_image, x, y): bg_pixel = bg_image.load()[x, y] fullbg_pixel = fullbg_image.load()[x, y] threshold = 60 if (abs(bg_pixel[0] - fullbg_pixel[0] < threshold) and abs(bg_pixel[1] - fullbg_pixel[1] < threshold) and abs( bg_pixel[2] - fullbg_pixel[2] < threshold)): return True else: return False 将完整图片和缺口图片两个对象分别赋值给变量 bg_image和 fullbg_image,接下来对比图片获取缺口。我们在这里遍历图片的每个坐标点,获取两张图片对应像素点的 RGB 数据,判断像素的各个颜色之差,abs() 用于取绝对值,如果二者的 RGB 数据差距在一定范围内,那就代表两个像素相同,继续比对下一个像素点,如果差距超过一定范围,则代表像素点不同,当前位置即为缺口位置 【13.13】构造移动轨迹函数123456789101112131415def get_trace(distance): trace = [] faster_distance = distance * (4 / 5) start, v0, t = 0, 0, 0.1 while start < distance: if start < faster_distance: a = 20 else: a = -20 move = v0 * t + 1 / 2 * a * t * t v = v0 + a * t v0 = v start += move trace.append(round(move)) return trace get_trace() 方法传入的参数为移动的总距离,返回的是运动轨迹,运动轨迹用 trace 表示,它是一个列表,列表的每个元素代表每次移动多少距离,利用 Selenium 进行对滑块的拖拽时要模仿人的行为,由于有个对准过程,所以是先快后慢,匀速移动、随机速度移动都不会成功,因此要设置一个加速和减速的距离,这里设置加速距离 faster_distance 是总距离 distance 的4/5倍,滑块滑动的加速度用 a 来表示,当前速度用 v 表示,初速度用 v0 表示,位移用 move 表示,所需时间用 t 表示,它们之间满足以下关系: 12move = v0 * t + 0.5 * a * t * t v = v0 + a * t 设置初始位置、初始速度、时间间隔分别为0, 0, 0.1,加速阶段和减速阶段的加速度分别设置为20和-20,直到运动轨迹达到总距离时,循环终止,最后得到的 trace 记录了每个时间间隔移动了多少位移,这样滑块的运动轨迹就得到了 【13.14】模拟拖动函数1234567def move_to_gap(trace): slider = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'div.geetest_slider_button'))) ActionChains(browser).click_and_hold(slider).perform() for x in trace: ActionChains(browser).move_by_offset(xoffset=x, yoffset=0).perform() time.sleep(0.5) ActionChains(browser).release().perform() 传入的参数为运动轨迹,首先查找到滑动按钮,然后调用 ActionChains 的 click_and_hold() 方法按住拖动底部滑块,perform() 方法用于执行,遍历运动轨迹获取每小段位移距离,调用 move_by_offset() 方法移动此位移,最后调用 release() 方法松开鼠标即可 【13.15】效果实现动图最终实现效果图:(关键信息已经过打码处理) 【13.16】完整代码123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138from selenium import webdriverfrom selenium.webdriver.chrome.options import Optionsfrom selenium.webdriver.support.wait import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECfrom selenium.webdriver.common.by import Byfrom selenium.webdriver import ActionChainsimport timeimport randomfrom PIL import Imagedef init(): global url, browser, username, password, wait url = 'https://passport.bilibili.com/login' path = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe' chrome_options = Options() chrome_options.add_argument('--start-maximized') browser = webdriver.Chrome(executable_path=path, chrome_options=chrome_options) username = '155********' password = '***********' wait = WebDriverWait(browser, 20)def login(): browser.get(url) user = wait.until(EC.presence_of_element_located((By.ID, 'login-username'))) passwd = wait.until(EC.presence_of_element_located((By.ID, 'login-passwd'))) user.send_keys(username) passwd.send_keys(password) login_btn = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'a.btn.btn-login'))) time.sleep(random.random() * 3) login_btn.click()def find_element(): c_background = wait.until( EC.presence_of_element_located((By.CSS_SELECTOR, 'canvas.geetest_canvas_bg.geetest_absolute'))) c_slice = wait.until( EC.presence_of_element_located((By.CSS_SELECTOR, 'canvas.geetest_canvas_slice.geetest_absolute'))) c_full_bg = wait.until( EC.presence_of_element_located((By.CSS_SELECTOR, 'canvas.geetest_canvas_fullbg.geetest_fade.geetest_absolute'))) hide_element(c_slice) save_screenshot(c_background, 'back') show_element(c_slice) save_screenshot(c_slice, 'slice') show_element(c_full_bg) save_screenshot(c_full_bg, 'full')def hide_element(element): browser.execute_script(\"arguments[0].style=arguments[1]\", element, \"display: none;\")def show_element(element): browser.execute_script(\"arguments[0].style=arguments[1]\", element, \"display: block;\")def save_screenshot(obj, name): try: pic_url = browser.save_screenshot('.\\\\bilibili.png') print(\"%s:截图成功!\" % pic_url) left = obj.location['x'] top = obj.location['y'] right = left + obj.size['width'] bottom = top + obj.size['height'] print('图:' + name) print('Left %s' % left) print('Top %s' % top) print('Right %s' % right) print('Bottom %s' % bottom) print('') im = Image.open('.\\\\bilibili.png') im = im.crop((left, top, right, bottom)) file_name = 'bili_' + name + '.png' im.save(file_name) except BaseException as msg: print(\"%s:截图失败!\" % msg)def slide(): distance = get_distance(Image.open('.\\\\bili_back.png'), Image.open('.\\\\bili_full.png')) print('计算偏移量为:%s Px' % distance) trace = get_trace(distance - 5) move_to_gap(trace) time.sleep(3)def get_distance(bg_image, fullbg_image): distance = 60 for i in range(distance, fullbg_image.size[0]): for j in range(fullbg_image.size[1]): if not is_pixel_equal(fullbg_image, bg_image, i, j): return idef is_pixel_equal(bg_image, fullbg_image, x, y): bg_pixel = bg_image.load()[x, y] fullbg_pixel = fullbg_image.load()[x, y] threshold = 60 if (abs(bg_pixel[0] - fullbg_pixel[0] < threshold) and abs(bg_pixel[1] - fullbg_pixel[1] < threshold) and abs( bg_pixel[2] - fullbg_pixel[2] < threshold)): return True else: return Falsedef get_trace(distance): trace = [] faster_distance = distance * (4 / 5) start, v0, t = 0, 0, 0.1 while start < distance: if start < faster_distance: a = 20 else: a = -20 move = v0 * t + 1 / 2 * a * t * t v = v0 + a * t v0 = v start += move trace.append(round(move)) return tracedef move_to_gap(trace): slider = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'div.geetest_slider_button'))) ActionChains(browser).click_and_hold(slider).perform() for x in trace: ActionChains(browser).move_by_offset(xoffset=x, yoffset=0).perform() time.sleep(0.5) ActionChains(browser).release().perform()if __name__ == '__main__': init() login() find_element() slide()","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫学习","slug":"Python3-学习笔记/爬虫学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫学习/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"滑动验证码","slug":"滑动验证码","permalink":"https://www.itrhx.com/tags/滑动验证码/"}]},{"title":"Python3 爬虫学习笔记 C12","slug":"A42-Python3-spider-C12","date":"2019-09-05T14:54:48.887Z","updated":"2019-09-24T12:40:54.127Z","comments":true,"path":"2019/09/05/A42-Python3-spider-C12/","link":"","permalink":"https://www.itrhx.com/2019/09/05/A42-Python3-spider-C12/","excerpt":"Python3 爬虫学习笔记第十二章 —— 【验证码对抗系列 — 图形验证码】","text":"Python3 爬虫学习笔记第十二章 —— 【验证码对抗系列 — 图形验证码】 【12.1】关于普通图形验证码普通图形验证码一般由四位纯数字、纯字母或者字母数字组合构成,是最常见的验证码,也是最简单的验证码,利用 tesserocr 或者 pytesseract 库即可识别此类验证码,前提是已经安装好 Tesseract-OCR 软件 【12.2】tesserocr 库识别验证码简单示例: 123456import tesserocrfrom PIL import Imageimage = Image.open('code.png')result = tesserocr.image_to_text(image)print(result) 新建一个 Image 对象,调用 tesserocr 的 image_to_text() 方法,传入 Image 对象即可完成识别,另一种方法: 12import tesserocrprint(tesserocr.file_to_text('code.png')) 【12.3】pytesseract 库识别验证码简单示例: 1234567import pytesseractfrom PIL import Imageimg = Image.open('code.png')img = img.convert('RGB')img.show()print(pytesseract.image_to_string(img)) pytesseract 的各种方法: get_tesseract_version:返回 Tesseract 的版本信息; image_to_string:将图像上的 Tesseract OCR 运行结果返回到字符串; image_to_boxes:返回包含已识别字符及其框边界的结果; image_to_data:返回包含框边界,置信度和其他信息的结果。需要 Tesseract 3.05+; image_to_osd:返回包含有关方向和脚本检测的信息的结果。 有关参数: image_to_data(image, lang='', config='', nice=0, output_type=Output.STRING) image:图像对象; lang:Tesseract 语言代码字符串; config:任何其他配置为字符串,例如:config=’–psm 6’; nice:修改 Tesseract 运行的处理器优先级。Windows不支持。尼斯调整了类似 unix 的流程的优点; output_type:类属性,指定输出的类型,默认为string。 lang 参数,常见语言代码如下: chi_sim:简体中文 chi_tra:繁体中文 eng:英文 rus:俄罗斯语 fra:法语 deu:德语 jpn:日语 【12.4】验证码处理利用 Image 对象的 convert() 方法传入不同参数可以对验证码做一些额外的处理,如转灰度、二值化等操作,经过处理过后的验证码会更加容易被识别,识别准确度更高,各种参数及含义: 1:1位像素,黑白,每字节一个像素存储; L:8位像素,黑白; P:8位像素,使用调色板映射到任何其他模式; RGB:3x8位像素,真彩色; RGBA:4x8位像素,带透明度掩模的真彩色; CMYK:4x8位像素,分色; YCbCr:3x8位像素,彩色视频格式; I:32位有符号整数像素; F:32位浮点像素。 示例: 12345678import pytesseractfrom PIL import Imageimage = Image.open('code.png')image = image.convert('L')image.show()result = pytesseract.image_to_string(image)print(result) Image 对象的 convert() 方法参数传入 L,即可将图片转化为灰度图像,转换前后对比: 12345678import pytesseractfrom PIL import Imageimage = Image.open('code.png')image = image.convert('1')image.show()result = pytesseract.image_to_string(image)print(result) Image 对象的 convert() 方法参数传入 1,即可将图片进行二值化处理,处理前后对比: 【12.5】tesserocr 与 pytesserocr 相关资料 tesserocr GitHub:https://github.com/sirfz/tesserocr tesserocr PyPI:https://pypi.python.org/pypi/tesserocr pytesserocr GitHub:https://github.com/madmaze/pytesseract pytesserocr PyPI:https://pypi.org/project/pytesseract/ Tesseract-OCR 下载地址:http://digi.bib.uni-mannheim.de/tesseract tesseract GitHub:https://github.com/tesseract-ocr/tesseract tesseract 语言包:https://github.com/tesseract-ocr/tessdata tesseract 文档:https://github.com/tesseract-ocr/tesseract/wiki/Documentation","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫学习","slug":"Python3-学习笔记/爬虫学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫学习/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"图形验证码","slug":"图形验证码","permalink":"https://www.itrhx.com/tags/图形验证码/"}]},{"title":"Python3 爬虫学习笔记 C11","slug":"A41-Python3-spider-C11","date":"2019-09-04T14:06:03.110Z","updated":"2019-09-24T12:40:45.074Z","comments":true,"path":"2019/09/04/A41-Python3-spider-C11/","link":"","permalink":"https://www.itrhx.com/2019/09/04/A41-Python3-spider-C11/","excerpt":"Python3 爬虫学习笔记第十一章 —— 【MongoDB数据储存】","text":"Python3 爬虫学习笔记第十一章 —— 【MongoDB数据储存】 【11.1】关于 MongoDBMongoDB 属于非关系型数据库,即 NoSQL(Not Only SQL),NoSQL 是基于键值对的,不需要经过 SQL 层的解析,数据之间没有耦合性,性能极高,非关系型数据库分为以下几种: 键值存储数据库:Redis、Voldemort、Oracle BDB 等; 列存储数据库:Cassandra、HBase、Riak 等; 文档型数据库:CouchDB、MongoDB 等; 图形数据库:Neo4J、InfoGrid、Infinite Graph 等。 【11.2】MongoDB 基本操作语句123456789101112131415161718192021222324252627282930# 创建数据库(如果数据库不存在就创建数据库, 存在就切换到指定的数据库)use DATABASE_NAME# 查看所有数据库show dbs# 查看当前所在数据库db# 删除当前数据库db.dropDatabase()# 删除集合db.COLLECTION_NAME.drop()# 创建集合db.createCollection(\"COLLECTION_NAME\")# 插入文档db.COLLECTION_NAME.insert(document)db.COLLECTION_NAME.save(document) # 更新文档db.COLLECTION_NAME.update()# 删除文档db.COLLECTION_NAME.remove()# 查询文档db.COLLECTION_NAME.find(query, projection) 【11.3】连接 MongoDB连接 MongoDB 需要导入 pymongo 库,使用 MongoClient() 方法,向其传入地址参数 host 和 端口参数 port 即可 123import pymongoclient = pymongo.MongoClient(host='localhost', port=27017) 也可以直接传入 MongoDB 的连接字符串: 123import pymongoclient = pymongo.MongoClient('mongodb://localhost:27017/') 【11.4】指定数据库使用以下语句皆可指定一个名为 spiders 的数据库: 1db = client.spiders 1db = client['spiders'] 【11.5】指定集合MongoDB 的每个数据库包含多个集合(collection),类似于关系型数据库 MySQL 中的数据表,使用以下语句皆可指定一个名为 students 的集合: 1collection = db.students 1collection = db['students'] 【11.6】插入数据12345678910111213import pymongoclient = pymongo.MongoClient(host='localhost', port=27017)db = client.spiderscollection = db.studentsstudents = { 'id': '17110105', 'name': 'TRHX', 'age': 20, 'gender': 'male'}result = collection.insert(students)print(result) 在 spiders 数据库的 students 集合里,新建一条学生数据,该数据以字典形式表示,调用 collection 的 insert() 方法插入数据,在 MongoDB 中,每条数据都有一个_id 属性来唯一标识。如果没有显式指明该属性,MongoDB 会自动产生一个 ObjectId 类型的_id 属性。insert() 方法会在执行后返回 _id 值,在 MongoDB 数据库里面可以看到已经成功插入数据,输出结果: 15d6f1a4b57b65e1547bb3c24 进阶操作:同时插入多条数据,以列表形式传递: 12345678910111213141516171819import pymongoclient = pymongo.MongoClient(host='localhost', port=27017)db = client.spiderscollection = db.studentsstudents1 = { 'id': '17110105', 'name': 'TRHX', 'age': 20, 'gender': 'male'}students2 = { 'id': '17110106', 'name': 'AAAA', 'age': 22, 'gender': 'male'}result = collection.insert([students1, students2])print(result) 输出结果: 1[ObjectId('5d6f2be3cd1721962218a709'), ObjectId('5d6f2be3cd1721962218a70a')] PyMongo 3.x 及以上版本中,推荐使用 insert_one() 和 insert_many() 方法来分别插入单条记录和多条记录,示例: 插入单条记录 1234567891011121314import pymongoclient = pymongo.MongoClient(host='localhost', port=27017)db = client.spiderscollection = db.studentsstudents = { 'id': '17110105', 'name': 'TRHX', 'age': 20, 'gender': 'male'}result = collection.insert_one(students)print(result)print(result.inserted_id) 返回的是 InsertOneResult 对象,调用其 inserted_id 属性获取_id: 12<pymongo.results.InsertOneResult object at 0x0000020ED91A5608>5d6f73940fe700c5a7ac19f0 插入多条记录 1234567891011121314151617181920import pymongoclient = pymongo.MongoClient(host='localhost', port=27017)db = client.spiderscollection = db.studentsstudents1 = { 'id': '17110105', 'name': 'TRHX', 'age': 20, 'gender': 'male'}students2 = { 'id': '17110106', 'name': 'AAAA', 'age': 22, 'gender': 'male'}result = collection.insert_many([students1, students2])print(result)print(result.inserted_ids) 返回的类型是 InsertManyResult,调用 inserted_ids 属性可以获取插入数据的_id 列表: 12<pymongo.results.InsertManyResult object at 0x0000021698DD36C8>[ObjectId('5d6f68598fa881c69b2e0006'), ObjectId('5d6f68598fa881c69b2e0007')] 【11.6】数据查询事先已经创建好 spiders 数据库和 students 集合,包含以下数据: 1234567891011121314151617181920212223_id:ObjectId(\"5d6f95d40828142f1dc35fa5\")id:\"17110105\"name:\"TRHX\"age:20gender:\"male\"_id:ObjectId(\"5d6f95d40828142f1dc35fa6\")id:\"17110106\"name:\"AAA\"age:20gender:\"male\"_id:ObjectId(\"5d6f95d40828142f1dc35fa7\")id:\"17110107\"name:\"BBB\"age:19gender:\"female\"_id:ObjectId(\"5d6f95d40828142f1dc35fa8\")id:\"17110108\"name:\"CCC\"age:22gender:\"male\" 查询方法一:利用 find_one() 或 find() 方法进行查询, find_one() 查询得到的是单个结果,find() 则返回一个生成器对象 1234567import pymongoclient = pymongo.MongoClient(host='localhost', port=27017)db = client.spiderscollection = db.studentsresult = collection.find_one({'name': 'TRHX'})print(result) 查询 name 为 TRHX 的数据,返回一个字典类型: 1{'_id': ObjectId('5d6f95d40828142f1dc35fa5'), 'id': '17110105', 'name': 'TRHX', 'age': 20, 'gender': 'male'} 查询方法二:根据 ObjectId 查询,查询时需要使用 bson 库里面的 objectid: 12345678import pymongofrom bson.objectid import ObjectIdclient = pymongo.MongoClient(host='localhost', port=27017)db = client.spiderscollection = db.studentsresult = collection.find_one({'_id': ObjectId('5d6f95d40828142f1dc35fa7')})print(result) 查询结果: 1{'_id': ObjectId('5d6f95d40828142f1dc35fa7'), 'id': '17110107', 'name': 'BBB', 'age': 19, 'gender': 'female'} 使用 find() 方法查询多条数据: 123456789import pymongoclient = pymongo.MongoClient(host='localhost', port=27017)db = client.spiderscollection = db.studentsresults = collection.find({'gender': 'male'})print(results)for result in results: print(result) find() 方法返回一个生成器对象,遍历得到所有数据,每条数据都是字典类型: 1234<pymongo.cursor.Cursor object at 0x00000191F69AAA90>{'_id': ObjectId('5d6f95d40828142f1dc35fa5'), 'id': '17110105', 'name': 'TRHX', 'age': 20, 'gender': 'male'}{'_id': ObjectId('5d6f95d40828142f1dc35fa6'), 'id': '17110106', 'name': 'AAA', 'age': 20, 'gender': 'male'}{'_id': ObjectId('5d6f95d40828142f1dc35fa8'), 'id': '17110108', 'name': 'CCC', 'age': 22, 'gender': 'male'} 在查询条件中加入比较符号进行查询,以下代码实现了年龄大于等于20的数据查询: 12345678import pymongoclient = pymongo.MongoClient(host='localhost', port=27017)db = client.spiderscollection = db.studentsresults = collection.find({'age': {'$gte': 20}})for result in results: print(result) 符号 $gte 表示大于等于,查询结果如下: 123{'_id': ObjectId('5d6f95d40828142f1dc35fa5'), 'id': '17110105', 'name': 'TRHX', 'age': 20, 'gender': 'male'}{'_id': ObjectId('5d6f95d40828142f1dc35fa6'), 'id': '17110106', 'name': 'AAA', 'age': 20, 'gender': 'male'}{'_id': ObjectId('5d6f95d40828142f1dc35fa8'), 'id': '17110108', 'name': 'CCC', 'age': 22, 'gender': 'male'} 附表:各种比较符号 符号 含义 示例 $lt 小于 {‘age’: {‘$lt’: 20}} $gt 大于 {‘age’: {‘$gt’: 20}} $lte 小于等于 {‘age’: {‘$lte’: 20}} $gte 大于等于 {‘age’: {‘$gte’: 20}} $ne 不等于 {‘age’: {‘$ne’: 20}} $in 在范围内 {‘age’: {‘$in’: [20, 23]}} $nin 不在范围内 {‘age’: {‘$nin’: [20, 23]}} 在查询条件中加入功能符号进行查询,以下代码用正则匹配实现了对名字以 T 开头的学生数据的查询: 12345678import pymongoclient = pymongo.MongoClient(host='localhost', port=27017)db = client.spiderscollection = db.studentsresults = collection.find({'name': {'$regex': '^T.*'}})for result in results: print(result) 查询结果: 1{'_id': ObjectId('5d6f95d40828142f1dc35fa5'), 'id': '17110105', 'name': 'TRHX', 'age': 20, 'gender': 'male'} 附表:各种功能符号 符号 含义 示例 示例含义 $regex 匹配正则表达式 {‘name’: {‘$regex’: ‘^T.*’}} name 以 T 开头 $exists 属性是否存在 {‘name’: {‘$exists’: True}} name 属性存在 $type 类型判断 {‘age’: {‘$type’: ‘int’}} age 的类型为 int $mod 数字模操作 {‘age’: {‘$mod’: [5, 0]}} 年龄模 5 余 0 $text 文本查询 {‘$text’: {‘$search’: ‘Mike’}} text 类型的属性中包含 Mike 字符串 $where 高级条件查询 {‘$where’: ‘obj.fans_count == obj.follows_count’} 自身粉丝数等于关注数 其他操作:https://docs.mongodb.com/manual/reference/operator/query/ 【11.7】数据计数调用 count() 方法可以统计查询结果有多少条数据,输出结果为一个整数: 1234567import pymongoclient = pymongo.MongoClient(host='localhost', port=27017)db = client.spiderscollection = db.studentsresult = collection.find({'name': {'$regex': '^T.*'}}).count()print(result) 【11.8】数据排序调用 sort() 方法,向其传入排序的字段及升降序标志即可完成排序: 123456789import pymongoclient = pymongo.MongoClient(host='localhost', port=27017)db = client.spiderscollection = db.studentsascending = collection.find().sort('name', pymongo.ASCENDING)descending = collection.find().sort('name', pymongo.DESCENDING)print('升序排列:', [result['name'] for result in ascending])print('降序排列:', [result['name'] for result in descending]) 输出结果: 12升序排列: ['AAA', 'BBB', 'CCC', 'TRHX']降序排列: ['TRHX', 'CCC', 'BBB', 'AAA'] 【11.9】数据偏移利用 skip() 方法偏移几个位置,就可以跳过前几条数据,获取偏移量之后的几个数据;利用 limit() 方法指定获取前几条数据: 123456789import pymongoclient = pymongo.MongoClient(host='localhost', port=27017)db = client.spiderscollection = db.studentsascending = collection.find().sort('name', pymongo.ASCENDING).skip(1)descending = collection.find().sort('name', pymongo.DESCENDING).limit(2)print('升序排列(偏移量为1,获取后三条数据):', [result['name'] for result in ascending])print('降序排列(限制获取前两条数据):', [result['name'] for result in descending]) 输出结果: 12升序排列(偏移量为1,获取后三条数据): ['BBB', 'CCC', 'TRHX']降序排列(限制获取前两条数据): ['TRHX', 'CCC'] 【11.10】更新数据使用 update() 方法,指定更新的条件和更新后的数据即可: 12345678910import pymongoclient = pymongo.MongoClient(host='localhost', port=27017)db = client.spiderscollection = db.studentscondition = {'name': 'TRHX'}student = collection.find_one(condition)student['age'] = 18result = collection.update(condition, student)print(result) 该代码将 name 为 TRHX 的 age 改为了 18,返回结果仍然是字典形式,ok 代表执行成功,nModified 代表影响的数据条数: 1{'n': 1, 'nModified': 1, 'ok': 1.0, 'updatedExisting': True} 进阶操作:使用 $set 操作符对数据进行更新,指定更新的条件和更新后的数据即可,这样做的好处是:只更新指定的 student 字典内存在的字段,如果原先还有其他字段,则不会更新,也不会删除;如果不用 $set ,则会把之前的数据全部用 student 字典替换,如果原本存在其他字段,则会被删除 12345678910import pymongoclient = pymongo.MongoClient(host='localhost', port=27017)db = client.spiderscollection = db.studentscondition = {'name': 'TRHX'}student = collection.find_one(condition)student['age'] = 18result = collection.update(condition, {'$set': student})print(result) 和插入数据的 insert() 方法一样,在 PyMongo 3.x 版本里,推荐使用 update_one() 和 update_many() 方法 1234567891011import pymongoclient = pymongo.MongoClient(host='localhost', port=27017)db = client.spiderscollection = db.studentscondition = {'name': 'TRHX'}student = collection.find_one(condition)student['age'] = 19result = collection.update_one(condition, {'$set': student})print(result)print(result.matched_count, result.modified_count) 注意:update_one() 方法不能直接传入修改后的字典,只能使用 {'$set': student} 的形式传入,可以调用 matched_count 和 modified_count 属性,获取匹配的数据条数和影响的数据条数: 12<pymongo.results.UpdateResult object at 0x00000235A1684508>1 1 使用update_many() 方法可以将所有符合条件的数据都更新: 123456789import pymongoclient = pymongo.MongoClient(host='localhost', port=27017)db = client.spiderscollection = db.studentscondition = {'age': {'$gt': 18}}result = collection.update_many(condition, {'$set': {'age': 25}})print(result)print(result.matched_count, result.modified_count) 匹配所有年龄大于 18 的数据,更新条件为将这些所有满足条件的年龄都设置成 25,输出结果如下: 12<pymongo.results.UpdateResult object at 0x00000285CECC45C8>4 4 【11.11】删除数据调用 remove() 方法并指定删除的条件,此时符合条件的所有数据均会被删除 1234567import pymongoclient = pymongo.MongoClient(host='localhost', port=27017)db = client.spiderscollection = db.studentsresult = collection.remove({'name': 'CCC'})print(result) 输出结果: 1{'n': 1, 'ok': 1.0} 同样的,在 PyMongo 3.x 版本里,推荐使用 delete_one() 和 delete_many() 方法 12345678910import pymongoclient = pymongo.MongoClient(host='localhost', port=27017)db = client.spiderscollection = db.studentsresult = collection.delete_one({'name': 'AAA'})print(result)print(result.deleted_count)result = collection.delete_many({'gender': 'female'})print(result.deleted_count) 调用 deleted_count 属性可以获取删除的数据条数,输出结果: 123<pymongo.results.DeleteResult object at 0x0000024441B245C8>11 PyMongo 官方文档:http://api.mongodb.com/python/current/api/pymongo/collection.html","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫学习","slug":"Python3-学习笔记/爬虫学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫学习/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"MongoDB","slug":"MongoDB","permalink":"https://www.itrhx.com/tags/MongoDB/"}]},{"title":"Python3 爬虫学习笔记 C10","slug":"A40-Python3-spider-C10","date":"2019-09-03T15:39:27.326Z","updated":"2020-03-14T06:21:36.668Z","comments":true,"path":"2019/09/03/A40-Python3-spider-C10/","link":"","permalink":"https://www.itrhx.com/2019/09/03/A40-Python3-spider-C10/","excerpt":"Python3 爬虫学习笔记第十章 —— 【MySQL数据储存】","text":"Python3 爬虫学习笔记第十章 —— 【MySQL数据储存】 【10.1】MySQL 基本操作语句安装完 MySQL 后,打开 MySQL x.x Command Line Client - Unicode,输入密码即可登录 MySQL,也可在 MySQL 安装目录下打开 cmd 使用命令登录数据库 数据库操作1234567891011121314151617# 连接数据库mysql -u root -p# 退出数据库exit# 查看所有的数据库SHOW DATABASES;# 创建一个数据库CREATE DATABASE X;# 删除一个数据库DROP DATABASE IF EXISTS X;# 使用这个数据库USE X; 表操作12345678910111213141516171819202122232425262728293031323334353637# 查看所有的表SHOW TABLES ;# 创建一个表CREATE TABLE n(id INT, name VARCHAR(10));CREATE TABLE m(id INT, name VARCHAR(10), PRIMARY KEY (id), FOREIGN KEY (id) REFERENCES n(id), UNIQUE (name));CREATE TABLE m(id INT, name VARCHAR(10));# 直接将查询结果导入或复制到新创建的表CREATE TABLE n SELECT * FROM m;# 新创建的表与一个存在的表的数据结构类似CREATE TABLE m LIKE n;# 创建一个临时表# 临时表将在你连接MySQL期间存在。当断开连接时,MySQL将自动删除表并释放所用的空间。也可手动删除。CREATE TEMPORARY TABLE l(id INT, name VARCHAR(10));# 直接将查询结果导入或复制到新创建的临时表CREATE TEMPORARY TABLE tt SELECT * FROM n;# 删除一个存在表DROP TABLE IF EXISTS m;# 更改存在表的名称ALTER TABLE n RENAME m;RENAME TABLE n TO m;# 查看表的结构(以下五条语句效果相同)DESC n;DESCRIBE n;SHOW COLUMNS IN n;SHOW COLUMNS FROM n;EXPLAIN n;# 查看表的创建语句SHOW CREATE TABLE n; 表的结构12345678910111213141516171819202122232425# 添加字段ALTER TABLE n ADD age VARCHAR(2);# 添加字段时设定位置ALTER TABLE n ADD age VARCHAR(2) FIRST;ALTER TABLE n ADD age VARCHAR(2) AFTER name;# 修改字段在表中的位置ALTER TABLE n MODIFY age VARCHAR(2) AFTER name;# 删除字段ALTER TABLE n DROP age;# 更改字段属性和属性ALTER TABLE n CHANGE age a INT;# 只更改字段属性ALTER TABLE n MODIFY age VARCHAR(7) ;# 改变表的存储引擎ALTER TABLE t ENGINE myisam;ALTER TABLE t ENGINE innodb;# 设定自增 初始为1,只能一个字段使用,该字段为主键的一部分ALTER TABLE t AUTO_INCREMENT = 0; 表的数据123456789101112131415# 增加数据INSERT INTO n VALUES (1, 'tom', '23'), (2, 'john', '22');INSERT INTO n SELECT * FROM n; # 把数据复制一遍重新插入# 删除数据DELETE FROM n WHERE id = 2;# 更改数据UPDATE n SET name = 'tom' WHERE id = 2;# 数据查找SELECT * FROM n WHERE name LIKE '%h%';# 数据排序(反序)SELECT * FROM n ORDER BY name, id DESC ; 【10.2】Python 连接 MySQL123456789import pymysqldb = pymysql.connect(host='localhost', user='root', password='000000', port=3306)cursor = db.cursor()cursor.execute('SELECT VERSION()')data = cursor.fetchone()print('Database version:', data)cursor.execute(\"CREATE DATABASE spiders DEFAULT CHARACTER SET utf8mb4\")db.close() 通过 PyMySQL 的 connect 方法声明一个 MySQL 连接对象 db,当前 MySQL 数据库运行在本地,设定 host='localhost',用户名为 root,登录密码为 000000,运行在 3306 端口,调用 cursor() 方法获得 MySQL 的操作游标,该游标用来执行 SQL 语句,通过游标操作 execute() 方法写入 SQL 语句,第一条 SQL 语句获取 MySQL 的版本信息,调用 fetchone() 方法获得第一条数据,即 MySQL 的版本号。第二条 SQL 语句执行创建 spiders 数据库的操作,编码为 utf8mb4,运行该段代码将输出 MySQL 的版本号: 1Database version: ('8.0.17',) 【10.3】创建表1234567import pymysqldb = pymysql.connect(host='localhost', user='root', password='000000', port=3306, db='spiders')cursor = db.cursor()sql = 'CREATE TABLE IF NOT EXISTS students (id VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, age VARCHAR(255) NOT NULL, PRIMARY KEY (id))'cursor.execute(sql)db.close() 该段代码实现了在 spiders 数据库里创建了一个名为 students 的表,包含 id、name、age 三个字段,类型依次为 varchar、varchar、int 【10.4】插入数据1234567891011121314import pymysqlid = '17110105'user = 'TRH'age = 20db = pymysql.connect(host='localhost', user='root', password='000000', port=3306, db='spiders')cursor = db.cursor()sql = 'INSERT INTO students(id, name, age) values(%s, %s, %s)'try: cursor.execute(sql, (id, user, age)) db.commit()except: db.rollback()db.close() commit() 方法的作用是实现数据插入,是真正将语句提交到数据库执行的方法,使用 try except 语句实现异常处理,如果执行失败,则调用 rollback() 方法执行数据回滚,保证原数据不被破坏,使用查询语句可以看到已经插入的数据: 进阶操作:将需要插入的数据构造成一个字典,这样的做法可以让插入方法无需改动,只需要传入一个动态变化的字典就行了,改写原来的代码如下: 123456789101112131415161718192021import pymysqldata = { 'id': '17110105', 'name': 'TRH', 'age': 20}table = 'students'keys = ', '.join(data.keys())values = ', '.join(['%s']*len(data))db = pymysql.connect(host='localhost', user='root', password='000000', port=3306, db='spiders')cursor = db.cursor()sql = 'INSERT INTO {table}({keys}) VALUES ({values})'.format(table=table, keys=keys, values=values)try: cursor.execute(sql, tuple(data.values())) print('数据插入成功!') db.commit()except: print('数据插入失败!') db.rollback()db.close() 传入的数是字典,将其定义为 data 变量,表名定义成变量 table,构造插入的字段 id、name 和 age。', '.join(data.keys()) 的结果就是 id, name, age,接着需要构造多个 %s 当作占位符,有三个字段,就需要构造 %s, %s, %s。首先定义长度为 1 的数组 ['%s'],然后用乘法将其扩充为 ['%s', '%s', '%s'],再调用 join() 方法,最终变成 %s, %s, %s。再利用字符串的 format() 方法将表名、字段名和占位符构造出来。最终的 SQL 语句就被动态构造成了如下语句: 1INSERT INTO students(id, name, age) VALUES (%s, %s, %s) 【10.5】更新数据1234567891011121314151617181920212223import pymysqldata = { 'id': '17110105', 'name': 'TRH', 'age': 21}table = 'students'keys = ', '.join(data.keys())values = ', '.join(['%s']*len(data))db = pymysql.connect(host='localhost', user='root', password='000000', port=3306, db='spiders')cursor = db.cursor()sql = 'INSERT INTO {table}({keys}) VALUES ({values}) ON DUPLICATE KEY UPDATE'.format(table=table, keys=keys, values=values)update = ','.join([\"{key} = % s\".format(key=key) for key in data])sql += updatetry: if cursor.execute(sql, tuple(data.values())*2): print('数据插入成功!') db.commit()except: print('数据插入失败!') db.rollback()db.close() ON DUPLICATE KEY UPDATE 表示如果主键已经存在,就执行更新操作,最终被构造成如下语句: 1INSERT INTO students(id, name, age) VALUES (% s, % s, % s) ON DUPLICATE KEY UPDATE id = % s, name = % s, age = % s 【10.6】删除数据123456789101112131415import pymysqltable = 'students'condition = 'age = 20'db = pymysql.connect(host='localhost', user='root', password='000000', port=3306, db='spiders')cursor = db.cursor()sql = 'DELETE FROM {table} WHERE {condition}'.format(table=table, condition=condition)try: cursor.execute(sql) print('数据删除成功!') db.commit()except: print('数据删除失败!') db.rollback()db.close() 删除操作直接使用 DELETE 语句,指定要删除的目标表名和删除条件即可 【10.7】查询数据123456789101112131415161718import pymysqltable = 'students'db = pymysql.connect(host='localhost', user='root', password='000000', port=3306, db='spiders')cursor = db.cursor()sql = 'SELECT * FROM students WHERE age >= 20'try: cursor.execute(sql) print('Count:', cursor.rowcount) one = cursor.fetchone() print('One:', one) results = cursor.fetchall() print('Results:', results) print('Results Type:', type(results)) for row in results: print(row)except: print('查询失败!') sql = 'SELECT * FROM students WHERE age >= 20':构造一条 SQL 语句,将年龄 大于等于20 岁的学生查询出来 cursor.rowcount:调用 cursor 的 rowcount 属性获取查询结果的条数 cursor.fetchone():调用 cursor 的 fetchone() 方法,获取结果的第一条数据,返回结果是元组形式,元组的元素顺序跟字段一一对应,即第一个元素就是第一个字段 id,第二个元素就是第二个字段 name,以此类推 cursor.fetchall():调用 cursor 的 fetchall() 方法,得到结果的所有数据,它是二重元组,每个元素都是一条记录,本例中显示的是 3 条数据而不是 4 条,这是因为它的内部实现有一个偏移指针用来指向查询结果,最开始偏移指针指向第一条数据,取一次之后,指针偏移到下一条数据,这样再取的话,就会取到下一条数据了。我们最初调用了一次 fetchone 方法,这样结果的偏移指针就指向下一条数据,fetchall 方法返回的是偏移指针指向的数据一直到结束的所有数据,所以该方法获取的结果就只剩 3 个了 【10.8】实战训练 — 爬取CSDN博客标题和地址保存到 MySQL利用 requests 库构建请求,BeautifulSoup 解析库解析网页,获取自己博客文章的标题和地址,将其储存到本地 MySQL 数据库中,事先已经创建好了一个 blog 数据库,并创建了一个名为 article 的数据表,数据表包含 id、title、url 三个字段,其中 id 的 AUTO_INCREMENT 属性可以使 id 自己增加,PRIMARY KEY 关键字用于将 id 定义为主键 创建 article 数据表: 1234567import pymysqldb = pymysql.connect(host='localhost', user='root', password='000000', port=3306, db='blog')cursor = db.cursor()sql = 'CREATE TABLE IF NOT EXISTS article (id INT NOT NULL AUTO_INCREMENT, title VARCHAR(255) NOT NULL, url VARCHAR(255) NOT NULL, PRIMARY KEY (id))'cursor.execute(sql)db.close() 获取文章标题和对应的 URL 并将其储存到 MySQL 中: 123456789101112131415161718192021222324import requestsimport pymysqlfrom bs4 import BeautifulSoupdb = pymysql.connect(host='localhost', user='root', password='000000', port=3306, db='blog')cursor = db.cursor()headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36',}url = \"https://blog.csdn.net/qq_36759224\"request = requests.get(url, headers=headers)soup = BeautifulSoup(request.text, 'lxml')title_list = soup.find_all('h4')for list in title_list: s = list.a.text.strip() title = s.replace('原', '') url = list.a['href'].strip() # print(title + url) cursor.execute('INSERT INTO article (title, url) VALUES (%s, %s)', (title, url))db.commit()print('数据写入完毕!')db.close() 在命令行中使用 SELECT * FROM article; 命令可以查看到数据已经成功获取并储存到了数据库中:","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫学习","slug":"Python3-学习笔记/爬虫学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫学习/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"MySQL","slug":"MySQL","permalink":"https://www.itrhx.com/tags/MySQL/"}]},{"title":"Python3 爬虫学习笔记 C09","slug":"A39-Python3-spider-C09","date":"2019-08-27T10:58:37.295Z","updated":"2019-10-09T10:40:08.565Z","comments":true,"path":"2019/08/27/A39-Python3-spider-C09/","link":"","permalink":"https://www.itrhx.com/2019/08/27/A39-Python3-spider-C09/","excerpt":"Python3 爬虫学习笔记第九章 —— 【文件储存】","text":"Python3 爬虫学习笔记第九章 —— 【文件储存】 用解析器解析出数据之后,还需要对数据进行保存。保存的形式多种多样,最简单的形式是直接保存为文本文件,如 TXT、JSON、CSV 等。 【9.1】TXT 文本存储TXT 文本存储的优点:操作非常简单,TXT 文本几乎兼容任何平台;缺点:不利于检索。 【9.1.1】基本示例1234567891011121314import requestsfrom lxml import etreeheaders = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36',}url = \"https://blog.csdn.net/qq_36759224\"request = requests.get(url, headers=headers)tree = etree.HTML(request.text)title_list = tree.xpath('//h4/a/text()')for title in title_list: with open('blog.txt', 'a', encoding='utf8') as fp: fp.write(title) 代码实现了我的 CSDN 博客首页所有博文标题的爬取,利用 requests 请求库发送请求,获取响应,用 XPath 获取每一篇博文的标题,然后写入 blog.txt 文件中: 123456789101112131415161718192021帝都的凛冬 最新屏蔽 CSDN 广告方法,专注阅读学习! 使用Github Pages和Hexo搭建自己的独立博客【超级详细的小白教程】 Python3 爬虫学习笔记 C08【解析库 Beautiful Soup】 Python3 爬虫学习笔记 C07 【解析库 lxml】 Python3 爬虫学习笔记 C06 【正则表达式】 Python3 爬虫学习笔记 C05 【Selenium + 无界面浏览器】 Python3 已经安装相关库,Pycharm 仍然报错 ModuleNotFoundError: No module named 'xxxxxx' 的解决办法 Windows/Android/iOS 等常见 User-Agent 大全 Selenium 显式等待条件及其含义 Python3 爬虫学习笔记 C04 【自动化测试工具 Selenium】 Python3 爬虫学习笔记 C03 【Ajax 数据爬取】 Python3 爬虫学习笔记 C02 【基本库 requests 的使用】 Python3 爬虫学习笔记 C01 【基本库 urllib 的使用】 利用官方支持为基于GitHub Pages的Hexo博客启用HTTPS 光学字符识别 Tesseract-OCR 的下载、安装和基本用法 Github+jsDelivr+PicGo 打造稳定快速、高效免费图床 利用Cloudflare为基于GitHub Pages的Hexo博客添加HTTPS支持 Python 中 if __name__ == '__main__': 的理解 Hexo 博客本地预览报错:Error: listen EADDRINUSE 0.0.0.0:4000 谷歌浏览器检查更新时出错:无法启动更新检查(错误代码为 3: 0x80080005 -- system level) 【9.1.2】打开方式open() 方法的第二个参数为打开方式,不同的打开方式如下: 读写方式 可否读写 若文件不存在 写入方式 r 读取 报错 不可写入 rb 以二进制方式读取 报错 不可写入 r+ 读取 + 写入 报错 覆盖写入 rb+ 以二进制方式读取+写入 报错 覆盖写入 w 写入 创建 覆盖写入 wb 以二进制方式写入 创建 覆盖写入 w+ 读取 + 写入 创建 覆盖写入 wb+ 以二进制方式读取+写入 创建 覆盖写入 a 写入 创建 附加写入 ab 以二进制方式写入 创建 附加写入 a+ 读取 + 写入 创建 附加写入 ab+ 以二进制方式读取+写入 创建 附加写入 【9.2】JSON 文件存储JSON,全称为 JavaScript Object Notation, 即 JavaScript 对象标记,它通过对象和数组的组合来表示数据,构造简洁但是结构化程度非常高,是一种轻量级的数据交换格式。 【9.2.1】对象和数组在 JavaScript 语言中,一切都是对象。因此,任何支持的类型都可以通过 JSON 来表示,例如字符串、数字、对象、数组等,但是对象和数组是比较特殊且常用的两种类型 对象:它在 JavaScript 中是使用花括号 {} 包裹起来的内容,数据结构为 {key1:value1, key2:value2, …} 的键值对结构。在面向对象的语言中,key 为对象的属性,value 为对应的值。键名可以使用整数和字符串来表示。值的类型可以是任意类型。 数组:数组在 JavaScript 中是方括号 [] 包裹起来的内容,数据结构为 [“java”, “javascript”, “vb”, …] 的索引结构。在 JavaScript 中,数组是一种比较特殊的数据类型,它也可以像对象那样使用键值对,但还是索引用得多。同样,值的类型可以是任意类型。 示例:一个 JSON 对象 123456789[{ \"name\": \"TRH\", \"gender\": \"male\", \"birthday\": \"1999-01-25\"}, { \"name\": \"XXX\", \"gender\": \"female\", \"birthday\": \"1999-10-18\"}] 【9.2.2】读取 JSONPython 里面的 JSON 库可以实现对 JSON 文件的读写操作,调用 JSON 库的 loads 方法将 JSON 文本字符串转为 JSON 对象、 dumps() 方法将 JSON 对象转为文本字符串 12345678910111213141516171819import jsonstr = '''[{ \"name\": \"TRH\", \"gender\": \"male\", \"birthday\": \"1999-01-25\"}, { \"name\": \"XXX\", \"gender\": \"female\", \"birthday\": \"1999-10-18\"}]'''data = json.loads(str)print(data)print(data[0]['name'])print(data[0].get('name'))print(data[0].get('age'))print(data[0].get('age', 25)) 使用 loads 方法将字符串转为 JSON 对象,通过索引来获取对应的内容,获取键值时有两种方式,一种是中括号加键名,另一种是通过 get 方法传入键名。使用 get 方法,如果键名不存在,则不会报错,会返回 None,get 方法还可以传入第二个参数(即默认值),尝试获取一个原字典中不存在的键名,此时默认会返回 None。如果传入第二个参数(即默认值),那么在不存在的情况下返回该默认值。 12345[{'name': 'TRH', 'gender': 'male', 'birthday': '1999-01-25'}, {'name': 'XXX', 'gender': 'female', 'birthday': '1999-10-18'}]TRHTRHNone25 【9.2.3】写入 JSON 文件调用 dumps 方法可以将 JSON 对象转化为字符串,然后再调用文件的 write 方法即可写入文本 123456789import jsondata = [{ 'name': 'TRH', 'gender': 'male', 'birthday': '1999-01-25'}]with open('data.json', 'w') as fp: fp.write(json.dumps(data)) data.json 文件: 1[{\"name\": \"TRH\", \"gender\": \"male\", \"birthday\": \"1999-01-25\"}] 添加参数 indent(代表缩进字符个数),将会格式化输出:123456789import jsondata = [{ 'name': 'TRH', 'gender': 'male', 'birthday': '1999-01-25'}]with open('data.json', 'w') as file: file.write(json.dumps(data, indent=2)) 输出结果: 1234567[ { \"name\": \"TRH\", \"gender\": \"male\", \"birthday\": \"1999-01-25\" }] 如果 JSON 中包含中文字符,需要指定参数 ensure_ascii 为 False,另外还要规定文件输出的编码: 123456789import jsondata = [{ 'name': '小明', 'gender': '男', 'birthday': '1999年01月25日'}]with open('data.json', 'w', encoding='utf-8') as file: file.write(json.dumps(data, indent=2, ensure_ascii=False)) 输出结果: 1234567[ { \"name\": \"小明\", \"gender\": \"男\", \"birthday\": \"1999年01月25日\" }] 【9.3】CSV 文本存储CSV(Comma-Separated Values)是逗号分隔值或字符分隔值的文件格式,其文件以纯文本的形式储存表格数据(数字和文本),CSV 文件的行与行之间用换行符分隔,列与列之间用逗号分隔 【9.3.1】写入12345678import csvwith open('data.csv', 'w') as csvfile: writer = csv.writer(csvfile) writer.writerow(['id', 'name', 'age']) writer.writerow(['10001', 'TRHX', 20]) writer.writerow(['10002', 'Bob', 22]) writer.writerow(['10003', 'Jordan', 21]) 打开 data.csv 文件,调用 CSV 库的 writer 方法初始化写入对象,然后调用 writerow 方法传入每行的数据即可完成写入,用 Excel 打开 data.csv 文件将是表格形式 1234567id,name,age10001,TRHX,2010002,Bob,2210003,Jordan,21 默认每一行之间是有一行空格的,可以使用参数 newline 来去除空行: 12345678import csvwith open('data.csv', 'w', newline='') as csvfile: writer = csv.writer(csvfile) writer.writerow(['id', 'name', 'age']) writer.writerow(['10001', 'TRHX', 20]) writer.writerow(['10002', 'Bob', 22]) writer.writerow(['10003', 'Jordan', 21]) 输出结果: 1234id,name,age10001,TRHX,2010002,Bob,2210003,Jordan,21 列与列之间的分隔符是可以修改的,只需要传入 delimiter 参数即可: 12345678import csvwith open('data.csv', 'w') as csvfile: writer = csv.writer(csvfile, delimiter=' ') writer.writerow(['id', 'name', 'age']) writer.writerow(['10001', 'TRHX', 20]) writer.writerow(['10002', 'Bob', 22]) writer.writerow(['10003', 'Jordan', 21]) 输出结果: 1234567id name age10001 TRHX 2010002 Bob 2210003 Jordan 21 调用 writerows 方法也可以同时写入多行,此时参数就需要为二维列表: 123456import csvwith open('data.csv', 'w') as csvfile: writer = csv.writer(csvfile, delimiter=' ') writer.writerow(['id', 'name', 'age']) writer.writerows([['10001', 'TRHX', 20], ['10002', 'Bob', 22], ['10003', 'Jordan', 21]]) 输出结果仍与原来的一样 此外 CSV 库中也提供了字典的写入方式: 123456789import csvwith open('data.csv', 'w') as csvfile: fieldnames = ['id', 'name', 'age'] writer = csv.DictWriter(csvfile, fieldnames=fieldnames) writer.writeheader() writer.writerow({'id': '10001', 'name': 'TRHX', 'age': 20}) writer.writerow({'id': '10002', 'name': 'Bob', 'age': 22}) writer.writerow({'id': '10003', 'name': 'Jordan', 'age': 21}) 首先定义 3 个字段,用 fieldnames 表示,然后将其传给 DictWriter 来初始化一个字典写入对象,接着可以调用 writeheader 方法先写入头信息,然后再调用 writerow 方法传入相应字典即可 1234567id,name,age10001,TRHX,2010002,Bob,2210003,Jordan,21 【9.3.2】读取有写入方法,同样也可以使用 csv 库来读取 CSV 文件: 123456import csvwith open('data.csv', 'r', encoding='utf-8') as csvfile: reader = csv.reader(csvfile) for row in reader: print(row) 构造 Reader 对象,遍历输出每行的内容,每一行都是一个列表形式。(如果 CSV 文件中包含中文的话,还需要指定文件编码)读取结果: 1234['id', 'name', 'age']['10001', 'TRHX', '20']['10002', 'Bob', '22']['10003', 'Jordan', '21'] 此外,还可以利用 pandas 的 read_csv 方法将数据从 CSV 中读取出来(pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。Pandas 纳入了大量库和一些标准的数据模型,提供了高效地操作大型数据集所需的工具) 1234import pandas as pddf = pd.read_csv('data.csv')print(df) 读取结果: 1234 id name age0 10001 TRHX 201 10002 Bob 222 10003 Jordan 21","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫学习","slug":"Python3-学习笔记/爬虫学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫学习/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"文件储存","slug":"文件储存","permalink":"https://www.itrhx.com/tags/文件储存/"}]},{"title":"Python3 爬虫学习笔记 C08","slug":"A38-Python3-spider-C08","date":"2019-08-26T13:57:58.870Z","updated":"2019-09-24T12:40:34.686Z","comments":true,"path":"2019/08/26/A38-Python3-spider-C08/","link":"","permalink":"https://www.itrhx.com/2019/08/26/A38-Python3-spider-C08/","excerpt":"Python3 爬虫学习笔记第八章 —— 【解析库 Beautiful Soup】","text":"Python3 爬虫学习笔记第八章 —— 【解析库 Beautiful Soup】 【8.1】关于 Beautiful SoupBeautiful Soup 可以从 HTML 或者 XML 文件中提取数据,Beautiful Soup 可以提供一些简单的、Python 式的函数用来处理导航、搜索、修改分析树等,它借助网页的结构和属性等特性来解析网页,lxml 只会局部遍历,而 Beautiful Soup 是基于 HTML DOM 的,会载入整个文档,解析整个 DOM 树,因此时间和内存开销都会大很多,所以性能要低于lxml 抓取工具 速度 使用难度 安装难度 正则 最快 困难 无(内置) lxml 快 简单 一般 BeautifulSoup 慢 最简单 简单 【8.2】Beautiful Soup 的基本使用需要使用命令 pip install bs4 安装库,Beautiful Soup 在解析时依赖解析器,除了支持 Python 标准库中的 HTML 解析器外,还支持一些第三方解析器: 解析器 使用方法 优势 劣势 Python 标准库 BeautifulSoup(markup, “html.parser”) Python 的内置标准库、执行速度适中 、文档容错能力强 Python 2.7.3 or 3.2.2) 前的版本中文容错能力差 LXML HTML 解析器 BeautifulSoup(markup, “lxml”) 速度快、文档容错能力强 需要安装 C 语言库 LXML XML 解析器 BeautifulSoup(markup, “xml”) 速度快、唯一支持 XML 的解析器 需要安装 C 语言库 html5lib BeautifulSoup(markup, “html5lib”) 最好的容错性、以浏览器的方式解析文档、生成 HTML5 格式的文档 速度慢、不依赖外部扩展 基本使用:1234from bs4 import BeautifulSoupsoup = BeautifulSoup('<p>Hello</p>', 'lxml')# soup = BeautifulSoup(open('soup.html', encoding='utf8'), 'lxml')print(soup.p.string) 输出结果:1Hello 【8.3】节点选择器直接调用节点的名称就可以选择节点元素,再调用 string 属性就可以得到节点内的文本 【8.3.1】元素选择新建 soup.html 文件:123456789101112131415161718192021222324252627282930313233343536<!DOCTYPE html><html lang=\"en\"><head> <meta charset=\"UTF-8\" /> <title>测试bs4</title></head><body><div> 甄姬 <p>百里守约</p> <p>李白</p> 太乙真人</div><div class=\"song\"> <p>李清照</p> <p>王安石</p> <p>苏轼</p> <p>柳宗元</p> <a href=\"http://www.song.com/\" title=\"赵匡义\" target=\"_self\">宋朝是最强大的王朝,不是军队的强大,而是经济很强大,国民都很有钱。</a> <img src=\"http://www.baidu.com/meinv.jpg\" alt=\"\"> <a href=\"\" class=\"du\">总为浮云能蔽日,长安不见使人愁</a></div><div class=\"tang\"> <ul> <li><a href=\"http://www.baidu.com\" title=\"qing\">清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村。</a> </li> <li><a href=\"http://www.163.com\" title=\"qin\">秦时明月汉时关,万里长征人未还,但使龙城飞将在,不教胡马度阴山。</a> </li> <li><a href=\"http://www.126.com\" alt=\"qi\">岐王宅里寻常见,崔九堂前几度闻,正是江南好风景,落花时节又逢君。</a> </li> <li><a href=\"http://www.sina.com\" class=\"du\">杜甫</a> </li> <li><b>唐朝</b></li> <li><i>宋朝</i></li> <li><a href=\"http://www.haha.com\" id=\"feng\">凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘。</a> </li> </ul></div></body></html> 1234567from bs4 import BeautifulSoupsoup = BeautifulSoup(open('soup.html', encoding='utf8'), 'lxml')print(soup.title)print(type(soup.title))print(soup.title.string)print(soup.head)print(soup.p) 依次查找 title、head、p 节点。输出结果:12345678<title>测试bs4</title><class 'bs4.element.Tag'>测试bs4<head><meta charset=\"utf-8\"/><title>测试bs4</title></head><p>百里守约</p> 【8.3.2】提取信息 string 属性:获取节点包含的文本值(如果标签里面还有标签,那么string获取到的结果为None) text 属性:获取节点包含的文本值 get_text() 属性:获取节点包含的文本值 name 属性:获取节点的名称 attrs :获取所有属性 attrs[‘属性名’] :获取指定属性 依然以 soup.html 为例:1234567891011121314from bs4 import BeautifulSoupsoup = BeautifulSoup(open('soup.html', encoding='utf8'), 'lxml')print(soup.title)print(soup.title.text)print(soup.title.get_text())print(soup.title.string)print(soup.div.string)print(soup.div.text)print(soup.title.name)print(soup.a['href']) # 获取href属性print(soup.a['title']) # 获取title属性print(soup.a['target']) # 获取target属性print(soup.a.attrs) # 获取所有属性print(soup.a.attrs['href']) # 获取href属性 输出结果:1234567891011121314151617<title>测试bs4</title>测试bs4测试bs4测试bs4None 甄姬 百里守约李白 太乙真人titlehttp://www.song.com/赵匡义_self{'href': 'http://www.song.com/', 'title': '赵匡义', 'target': '_self'}http://www.song.com/ 【8.3.3】嵌套选择12345678910from bs4 import BeautifulSouphtml = \"\"\"<html><head><title>This is a demo</title></head><body>\"\"\"soup = BeautifulSoup(html, 'lxml')print(soup.head.title)print(type(soup.head.title))print(soup.head.title.string) 获取 head 节点里面的 title 节点,输出结果:123<title>This is a demo</title><class 'bs4.element.Tag'>This is a demo 【8.3.4】关联选择 contents 属性:获取某个节点元素的直接子节点 children 属性:遍历某个节点元素的子节点 descendants 属性:获取某个节点元素所有的子孙节点 parent 属性:获取某个节点元素的父节点 parents 属性:获取某个节点元素所有的祖先节点 next_sibling 属性:获取节点的下一个兄弟元素 previous_sibling 属性:获取节点的上一个兄弟元素 next_siblings 属性:获取某个节点所有后面的兄弟元素 previous_siblings 属性:获取某个节点所有前面的兄弟元素 contents 属性应用示例 12345678910111213141516171819202122from bs4 import BeautifulSouphtml = \"\"\"<html> <head> <title>The Dormouse's story</title> </head> <body> <p class=\"story\"> Once upon a time there were three little sisters; and their names were <a href=\"http://example.com/elsie\" class=\"sister\" id=\"link1\"> <span>Elsie</span> </a> <a href=\"http://example.com/lacie\" class=\"sister\" id=\"link2\">Lacie</a> and <a href=\"http://example.com/tillie\" class=\"sister\" id=\"link3\">Tillie</a> and they lived at the bottom of a well. </p> <p class=\"story\">...</p>\"\"\"soup = BeautifulSoup(html, 'lxml')print(soup.p.contents) 获取 p 节点元素的直接子节点,输出结果:123['\\n Once upon a time there were three little sisters; and their names were\\n ', <a class=\"sister\" href=\"http://example.com/elsie\" id=\"link1\"><span>Elsie</span></a>, '\\n', <a class=\"sister\" href=\"http://example.com/lacie\" id=\"link2\">Lacie</a>, ' \\n and\\n ', <a class=\"sister\" href=\"http://example.com/tillie\" id=\"link3\">Tillie</a>, '\\n and they lived at the bottom of a well.\\n '] children 属性应用示例: 123456789101112131415161718192021222324from bs4 import BeautifulSouphtml = \"\"\"<html> <head> <title>The Dormouse's story</title> </head> <body> <p class=\"story\"> Once upon a time there were three little sisters; and their names were <a href=\"http://example.com/elsie\" class=\"sister\" id=\"link1\"> <span>Elsie</span> </a> <a href=\"http://example.com/lacie\" class=\"sister\" id=\"link2\">Lacie</a> and <a href=\"http://example.com/tillie\" class=\"sister\" id=\"link3\">Tillie</a> and they lived at the bottom of a well. </p> <p class=\"story\">...</p>\"\"\"soup = BeautifulSoup(html, 'lxml')print(soup.p.children)for i, child in enumerate(soup.p.children): print(i, child) 遍历 p 节点元素的子节点,输出结果:12345678910111213141516<list_iterator object at 0x00000228E3C205F8>0 Once upon a time there were three little sisters; and their names were 1 <a class=\"sister\" href=\"http://example.com/elsie\" id=\"link1\"><span>Elsie</span></a>2 3 <a class=\"sister\" href=\"http://example.com/lacie\" id=\"link2\">Lacie</a>4 and 5 <a class=\"sister\" href=\"http://example.com/tillie\" id=\"link3\">Tillie</a>6 and they lived at the bottom of a well. descendants 属性应用示例:123456789101112131415161718192021222324from bs4 import BeautifulSouphtml = \"\"\"<html> <head> <title>The Dormouse's story</title> </head> <body> <p class=\"story\"> Once upon a time there were three little sisters; and their names were <a href=\"http://example.com/elsie\" class=\"sister\" id=\"link1\"> <span>Elsie</span> </a> <a href=\"http://example.com/lacie\" class=\"sister\" id=\"link2\">Lacie</a> and <a href=\"http://example.com/tillie\" class=\"sister\" id=\"link3\">Tillie</a> and they lived at the bottom of a well. </p> <p class=\"story\">...</p>\"\"\"soup = BeautifulSoup(html, 'lxml')print(soup.p.descendants)for i, child in enumerate(soup.p.descendants): print(i, child)获取 p 节点元素所有的子孙节点,输出结果:123456789101112131415161718192021222324<generator object descendants at 0x0000018404A4C3B8>0 Once upon a time there were three little sisters; and their names were 1 <a class=\"sister\" href=\"http://example.com/elsie\" id=\"link1\"><span>Elsie</span></a>2 3 <span>Elsie</span>4 Elsie5 6 7 <a class=\"sister\" href=\"http://example.com/lacie\" id=\"link2\">Lacie</a>8 Lacie9 and 10 <a class=\"sister\" href=\"http://example.com/tillie\" id=\"link3\">Tillie</a>11 Tillie12 and they lived at the bottom of a well.parent 属性应用示例:123456789101112131415161718from bs4 import BeautifulSouphtml = \"\"\"<html> <head> <title>The Dormouse's story</title> </head> <body> <p class=\"story\"> Once upon a time there were three little sisters; and their names were <a href=\"http://example.com/elsie\" class=\"sister\" id=\"link1\"> <span>Elsie</span> </a> </p> </body></html>\"\"\"soup = BeautifulSoup(html, 'lxml')print(soup.a.parent)获取 a 节点元素的父节点,输出结果:123456<p class=\"story\"> Once upon a time there were three little sisters; and their names were <a class=\"sister\" href=\"http://example.com/elsie\" id=\"link1\"><span>Elsie</span></a></p>parents 属性应用示例:1234567891011121314from bs4 import BeautifulSouphtml = \"\"\"<html> <body> <p class=\"story\"> <a href=\"http://example.com/elsie\" class=\"sister\" id=\"link1\"> <span>Elsie</span> </a> </p>\"\"\"soup = BeautifulSoup(html, 'lxml')print(type(soup.a.parents))print(list(enumerate(soup.a.parents)))获取 a 节点元素所有的祖先节点,输出结果:1234567891011121314151617181920212223242526<class 'generator'>[(0, <p class=\"story\"><a class=\"sister\" href=\"http://example.com/elsie\" id=\"link1\"><span>Elsie</span></a></p>), (1, <body><p class=\"story\"><a class=\"sister\" href=\"http://example.com/elsie\" id=\"link1\"><span>Elsie</span></a></p></body>), (2, <html><body><p class=\"story\"><a class=\"sister\" href=\"http://example.com/elsie\" id=\"link1\"><span>Elsie</span></a></p></body></html>), (3, <html><body><p class=\"story\"><a class=\"sister\" href=\"http://example.com/elsie\" id=\"link1\"><span>Elsie</span></a></p></body></html>)]next_sibling、previous_sibling、next_siblings、previous_siblings 属性应用示例: 1234567891011121314151617181920212223html = \"\"\"<html> <body> <p class=\"story\"> Once upon a time there were three little sisters; and their names were <a href=\"http://example.com/elsie\" class=\"sister\" id=\"link1\"> <span>Elsie</span> </a> Hello <a href=\"http://example.com/lacie\" class=\"sister\" id=\"link2\">Lacie</a> and <a href=\"http://example.com/tillie\" class=\"sister\" id=\"link3\">Tillie</a> and they lived at the bottom of a well. </p> </body></html>\"\"\"from bs4 import BeautifulSoupsoup = BeautifulSoup(html, 'lxml')print('Next Sibling', soup.a.next_sibling)print('Prev Sibling', soup.a.previous_sibling)print('Next Siblings', list(enumerate(soup.a.next_siblings)))print('Prev Siblings', list(enumerate(soup.a.previous_siblings))) next_sibling 和 previous_sibling 分别获取 a 节点的下一个和上一个兄弟元素,next_siblings 和 previous_siblings 则分别返回 a 节点后面和前面的兄弟节点,输出结果: 12345678Next Sibling Hello Prev Sibling Once upon a time there were three little sisters; and their names were Next Siblings [(0, '\\n Hello\\n '), (1, <a class=\"sister\" href=\"http://example.com/lacie\" id=\"link2\">Lacie</a>), (2, ' \\n and\\n '), (3, <a class=\"sister\" href=\"http://example.com/tillie\" id=\"link3\">Tillie</a>), (4, '\\n and they lived at the bottom of a well.\\n ')]Prev Siblings [(0, '\\n Once upon a time there were three little sisters; and their names were\\n ')] 【8.4】方法选择器节点选择器直接调用节点的名称就可以选择节点元素,如果进行比较复杂的选择的话,方法选择器是一个不错的选择,它更灵活,常见的方法有 find_all、find 等,调用它们,直接传入相应的参数,就可以灵活查询了。 【8.4.1】find_all() 方法find_all 方法可以查询所有符合条件的元素,给它传入一些属性或文本来得到符合条件的元素。find_all 方法的 API:find_all(name , attrs , recursive , text , **kwargs)新建 soup.html:123456789101112131415161718192021222324252627282930313233343536<!DOCTYPE html><html lang=\"en\"><head> <meta charset=\"UTF-8\" /> <title>测试bs4</title></head><body><div> 甄姬 <p>百里守约</p> <p>李白</p> 太乙真人</div><div class=\"song\"> <p>李清照</p> <p>王安石</p> <p>苏轼</p> <p>柳宗元</p> <a href=\"http://www.song.com/\" title=\"赵匡义\" target=\"_self\">宋朝是最强大的王朝,不是军队的强大,而是经济很强大,国民都很有钱。</a> <img src=\"http://www.baidu.com/meinv.jpg\" alt=\"\"> <a href=\"\" class=\"du\">总为浮云能蔽日,长安不见使人愁</a></div><div class=\"tang\"> <ul> <li><a href=\"http://www.baidu.com\" title=\"qing\">清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村。</a> </li> <li><a href=\"http://www.163.com\" title=\"qin\">秦时明月汉时关,万里长征人未还,但使龙城飞将在,不教胡马度阴山。</a> </li> <li><a href=\"http://www.126.com\" name=\"qi\">岐王宅里寻常见,崔九堂前几度闻,正是江南好风景,落花时节又逢君。</a> </li> <li><a href=\"http://www.sina.com\" class=\"du\">杜甫</a> </li> <li><b>唐朝</b></li> <li><i>宋朝</i></li> <li><a href=\"http://www.haha.com\" id=\"feng\">凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘。</a> </li> </ul></div></body></html> 示例代码:12345678910from bs4 import BeautifulSoupsoup = BeautifulSoup(open('soup.html', encoding='utf8'), 'lxml')print(soup.find_all('a'), '\\n')print(soup.find_all('a')[1], '\\n')print(soup.find_all('a')[1].text, '\\n')print(soup.find_all(['a', 'b', 'i']), '\\n')print(soup.find_all('a', limit=2), '\\n')print(soup.find_all(title='qing'), '\\n')print(soup.find_all(attrs={'id': 'feng'}), '\\n') 输出结果:12345678910111213[<a href=\"http://www.song.com/\" target=\"_self\" title=\"赵匡义\">宋朝是最强大的王朝,不是军队的强大,而是经济很强大,国民都很有钱。</a>, <a class=\"du\" href=\"\">总为浮云能蔽日,长安不见使人愁</a>, <a href=\"http://www.baidu.com\" title=\"qing\">清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村。</a>, <a href=\"http://www.163.com\" title=\"qin\">秦时明月汉时关,万里长征人未还,但使龙城飞将在,不教胡马度阴山。</a>, <a href=\"http://www.126.com\" name=\"qi\">岐王宅里寻常见,崔九堂前几度闻,正是江南好风景,落花时节又逢君。</a>, <a class=\"du\" href=\"http://www.sina.com\">杜甫</a>, <a href=\"http://www.haha.com\" id=\"feng\">凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘。</a>] <a class=\"du\" href=\"\">总为浮云能蔽日,长安不见使人愁</a> 总为浮云能蔽日,长安不见使人愁 [<a href=\"http://www.song.com/\" target=\"_self\" title=\"赵匡义\">宋朝是最强大的王朝,不是军队的强大,而是经济很强大,国民都很有钱。</a>, <a class=\"du\" href=\"\">总为浮云能蔽日,长安不见使人愁</a>, <a href=\"http://www.baidu.com\" title=\"qing\">清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村。</a>, <a href=\"http://www.163.com\" title=\"qin\">秦时明月汉时关,万里长征人未还,但使龙城飞将在,不教胡马度阴山。</a>, <a href=\"http://www.126.com\" name=\"qi\">岐王宅里寻常见,崔九堂前几度闻,正是江南好风景,落花时节又逢君。</a>, <a class=\"du\" href=\"http://www.sina.com\">杜甫</a>, <b>唐朝</b>, <i>宋朝</i>, <a href=\"http://www.haha.com\" id=\"feng\">凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘。</a>] [<a href=\"http://www.song.com/\" target=\"_self\" title=\"赵匡义\">宋朝是最强大的王朝,不是军队的强大,而是经济很强大,国民都很有钱。</a>, <a class=\"du\" href=\"\">总为浮云能蔽日,长安不见使人愁</a>] [<a href=\"http://www.baidu.com\" title=\"qing\">清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村。</a>] [<a href=\"http://www.haha.com\" id=\"feng\">凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘。</a>] 【8.4.2】find() 方法find() 方法使用方法与 find_all() 方法相同,不同的是,find 方法返回的是单个元素,也就是第一个匹配的元素,而 find_all 返回的是所有匹配的元素组成的列表特别的: find_parents 和 find_parent:前者返回所有祖先节点,后者返回直接父节点。 find_next_siblings 和 find_next_sibling:前者返回后面所有的兄弟节点,后者返回后面第一个兄弟节点。 find_previous_siblings 和 find_previous_sibling:前者返回前面所有的兄弟节点,后者返回前面第一个兄弟节点。 find_all_next 和 find_next:前者返回节点后所有符合条件的节点,后者返回第一个符合条件的节点。 find_all_previous 和 find_previous:前者返回节点前所有符合条件的节点,后者返回第一个符合条件的节点。 【8.5】CSS 选择器使用 CSS 选择器,只需要调用 select 方法,传入相应的 CSS 选择器即可新建 soup.html 文件:1234567891011121314<!DOCTYPE html><html lang=\"en\"><div class=\"tang\"> <ul> <li><a href=\"http://www.baidu.com\" title=\"qing\">清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村。</a> </li> <li><a href=\"http://www.163.com\" title=\"qin\">秦时明月汉时关,万里长征人未还,但使龙城飞将在,不教胡马度阴山。</a> </li> <li><a href=\"http://www.126.com\" name=\"qi\">岐王宅里寻常见,崔九堂前几度闻,正是江南好风景,落花时节又逢君。</a> </li> <li><a href=\"http://www.sina.com\" class=\"du\">杜甫</a> </li> <li><a href=\"http://www.haha.com\" id=\"feng\">凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘。</a> </li> </ul></div></body></html> 通过 CSS 选择器依次选择 class=”tang” 的 div 节点下的 a 节点、id 为 feng 的节点以及其 href 元素:1234567from bs4 import BeautifulSoupsoup = BeautifulSoup(open('soup.html', encoding='utf8'), 'lxml')print(soup.select('li'), '\\n')print(soup.select('.tang > ul > li > a')[2], '\\n')print(soup.select('#feng')[0].text, '\\n')print(soup.select('#feng')[0]['href'], '\\n') 输出结果:1234567[<li><a href=\"http://www.baidu.com\" title=\"qing\">清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村。</a> </li>, <li><a href=\"http://www.163.com\" title=\"qin\">秦时明月汉时关,万里长征人未还,但使龙城飞将在,不教胡马度阴山。</a> </li>, <li><a href=\"http://www.126.com\" name=\"qi\">岐王宅里寻常见,崔九堂前几度闻,正是江南好风景,落花时节又逢君。</a> </li>, <li><a class=\"du\" href=\"http://www.sina.com\">杜甫</a> </li>, <li><a href=\"http://www.haha.com\" id=\"feng\">凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘。</a> </li>] <a href=\"http://www.126.com\" name=\"qi\">岐王宅里寻常见,崔九堂前几度闻,正是江南好风景,落花时节又逢君。</a> 凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘。 http://www.haha.com 附表:CSS 选择器,来源:https://www.w3school.com.cn/cssref/css_selectors.asp 选择器 例子 例子描述 CSS .class .intro 选择 class=”intro” 的所有元素 1 #id #firstname 选择 id=”firstname” 的所有元素 1 * * 选择所有元素 2 element p 选择所有 元素 1 element,element div,p 选择所有 元素和所有 元素 1 element element div p 选择 元素内部的所有 元素 1 element>element div>p 选择父元素为 元素的所有 元素 2 element+element div+p 选择紧接在 元素之后的所有 元素 2 [attribute] [target] 选择带有 target 属性所有元素 2 [attribute=value] [target=_blank] 选择 target=”_blank” 的所有元素 2 [attribute~=value] [title~=flower] 选择 title 属性包含单词 “flower” 的所有元素 2 [attribute =value] [lang =en] 选择 lang 属性值以 “en” 开头的所有元素 2 :link a:link 选择所有未被访问的链接 1 :visited a:visited 选择所有已被访问的链接 1 :active a:active 选择活动链接 1 :hover a:hover 选择鼠标指针位于其上的链接 1 :focus input:focus 选择获得焦点的 input 元素 2 :first-letter p:first-letter 选择每个 元素的首字母 1 :first-line p:first-line 选择每个 元素的首行 1 :first-child p:first-child 选择属于父元素的第一个子元素的每个 元素 2 :before p:before 在每个 元素的内容之前插入内容 2 :after p:after 在每个 元素的内容之后插入内容 2 :lang(language) p:lang(it) 选择带有以 “it” 开头的 lang 属性值的每个 元素 2 element1~element2 p~ul 选择前面有 元素的每个 元素 3 [attribute^=value] a[src^=”https”] 选择其 src 属性值以 “https” 开头的每个 元素 3 [attribute$=value] a[src$=”.pdf”] 选择其 src 属性以 “.pdf” 结尾的所有 元素 3 [attribute*=value] a[src*=”abc”] 选择其 src 属性中包含 “abc” 子串的每个 元素 3 :first-of-type p:first-of-type 选择属于其父元素的首个 元素的每个 元素 3 :last-of-type p:last-of-type 选择属于其父元素的最后 元素的每个 元素 3 :only-of-type p:only-of-type 选择属于其父元素唯一的 元素的每个 元素 3 :only-child p:only-child 选择属于其父元素的唯一子元素的每个 元素 3 :nth-child(n) p:nth-child(2) 选择属于其父元素的第二个子元素的每个 元素 3 :nth-last-child(n) p:nth-last-child(2) 同上,从最后一个子元素开始计数 3 :nth-of-type(n) p:nth-of-type(2) 选择属于其父元素第二个 元素的每个 元素 3 :nth-last-of-type(n) p:nth-last-of-type(2) 同上,但是从最后一个子元素开始计数 3 :last-child p:last-child 选择属于其父元素最后一个子元素每个 元素 3 :root :root 选择文档的根元素 3 :empty p:empty 选择没有子元素的每个 元素(包括文本节点) 3 :target #news:target 选择当前活动的 #news 元素 3 :enabled input:enabled 选择每个启用的 元素 3 :disabled input:disabled 选择每个禁用的 元素 3 :checked input:checked 选择每个被选中的 元素 3 :not(selector) :not(p) 选择非 元素的每个元素 3 ::selection ::selection 选择被用户选取的元素部分 3 【8.6】附表:Beautiful Soup 库 soup 对象常用属性与方法 基本元素 说明 返回类型 tag soup.a bs4.element.Tag name soup.a.name str attrs soup.a.attrs dict contents 子节点 list children 遍历子节点 list_iterator descendants 遍历所有子孙节点 generator parent 返回父亲标签 bs4.element.Tag parents 上行遍历父辈标签 generator prettify() 添加/n str find_all(name,attr) soup.find_all(‘a’)/([‘a’,‘b’])/(True)/(‘p’,‘course’)/(id=‘link1’)/(string=‘python’) bs4.element.ResultSet find() soup.find(‘a’)/返回第一个a标签 bs4.element.Tag","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫学习","slug":"Python3-学习笔记/爬虫学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫学习/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"Beautiful Soup","slug":"Beautiful-Soup","permalink":"https://www.itrhx.com/tags/Beautiful-Soup/"}]},{"title":"Python3 爬虫学习笔记 C07","slug":"A37-Python3-spider-C07","date":"2019-08-25T11:31:18.872Z","updated":"2019-09-24T12:40:30.940Z","comments":true,"path":"2019/08/25/A37-Python3-spider-C07/","link":"","permalink":"https://www.itrhx.com/2019/08/25/A37-Python3-spider-C07/","excerpt":"Python3 爬虫学习笔记第七章 —— 【解析库 lxml】","text":"Python3 爬虫学习笔记第七章 —— 【解析库 lxml】 【7.1】关于 lxml lxml 是 Python 的一个解析库,支持 HTML 和 XML 的解析,支持 XPath 解析方式,解析效率非常高,使用前需要用命令 pip3 install lxml 安装 lxml 库 【7.2】使用 XPath XPath(XML Path Language)即 XML 路径语言, lxml 解析库使用的正是 XPath 语法,最初是用来搜寻 XML 文档的,是一门在 XML 文档中查找信息的语言,它同样适用于 HTML 文档的搜索 XPath 常用规则 表达式 描述 nodename 选取此节点的所有子节点 / 从当前节点选取直接子节点 // 从当前节点选取子孙节点 . 选取当前节点 .. 选取当前节点的父节点 @ 选取属性 * 通配符,选择所有元素节点与元素名 @* 选取所有属性 [@attrib] 选取具有给定属性的所有元素 [@attrib=’value’] 选取给定属性具有给定值的所有元素 [tag] 选取所有具有指定元素的直接子节点 [tag=’text’] 选取所有具有指定元素并且文本内容是text节点 浏览器插件 XPath Helper,在线验证 XPath,谷歌商店下载地址:https://chrome.google.com/webstore/detail/hgimnogjllphhhkhlmebbmlgjoejdpjl XPath 基本使用方法:首先使用代码 from lxml import etree导入库,然后将 HTML 文档变成一个对象,再调用对象的方法去查找指定的节点,方法有两种:tree = etree.parse() 为本地文件查找,tree = etree.HTML() 为网络文件查找,再使用语句 tree.xpath() 查找指定节点。 【7.3】查找所有节点 新建一个 xpath.html 本地文件,内容如下: 123456789101112131415161718192021222324252627282930313233 <!DOCTYPE html><html lang=\"en\"><head> <meta charset=\"UTF-8\" /> <title>xpath测试</title></head><body><div class=\"song\"> 火药 <b>指南针</b> <b>印刷术</b> 造纸术</div><div class=\"tang\"> <ul> <li class=\"balove\">停车坐爱枫林晚,霜叶红于二月花。</li> <li id=\"hua\">商女不知亡国恨,隔江犹唱后庭花。</li> <li class=\"love\" name=\"yang\">一骑红尘妃子笑,无人知是荔枝来。</li> <li id=\"bei\">葡萄美酒夜光杯,欲饮琵琶马上催。</li> <li><a href=\"http://www.baidu.com/\">百度一下</a> </li> </ul> <ol> <li class=\"balucy\">寻寻觅觅冷冷清清,凄凄惨惨戚戚。</li> <li class=\"lily\">咋暖还寒时候,最难将息。</li> <li class=\"lilei\">三杯两盏淡酒。</li> <li>怎敌他晚来风急。</li> <li>雁过也,正伤心,却是旧时相识。</li> <li>爱情三十六计</li> <li>什么是爱情</li> </ol></div></body></html> 查找所有节点:12345from lxml import etreehtml = etree.parse('./xpath.html')result = html.xpath('//*')print(result) 使用 * 代表匹配所有节点,整个 xpath.html 文件中的所有节点都会被获取到,返回形式是一个列表,每个元素是 Element 类型,其后跟了节点的名称,如 html、body、div、ul、li、a 等,所有节点都包含在列表中,输出结果如下:1[<Element html at 0x1a836a34508>, <Element head at 0x1a836a344c8>, <Element meta at 0x1a836a345c8>, <Element title at 0x1a836a34608>, <Element body at 0x1a836a34648>, <Element div at 0x1a836a346c8>, <Element b at 0x1a836a34708>, <Element b at 0x1a836a34748>, <Element div at 0x1a836a34788>, <Element ul at 0x1a836a34688>, <Element li at 0x1a836a347c8>, <Element li at 0x1a836a34808>, <Element li at 0x1a836a34848>, <Element li at 0x1a836a34888>, <Element li at 0x1a836a348c8>, <Element a at 0x1a836a34908>, <Element ol at 0x1a836a34948>, <Element li at 0x1a836a34988>, <Element li at 0x1a836a349c8>, <Element li at 0x1a836a34a08>, <Element li at 0x1a836a34a48>, <Element li at 0x1a836a34a88>, <Element li at 0x1a836a34ac8>, <Element li at 0x1a836a34b08>] 【7.4】查找子节点 通过 / 或 // 即可查找元素的子节点或子孙节点: 12345 from lxml import etreehtml = etree.parse('./xpath.html')result = html.xpath('//ul/li')print(result) 选择 ul 节点的所有直接 li 子节点:1[<Element li at 0x2a094d044c8>, <Element li at 0x2a094d045c8>, <Element li at 0x2a094d04608>, <Element li at 0x2a094d04648>, <Element li at 0x2a094d04688>] 【7.5】查找父节点 知道了子节点,也可以用 .. 或者 parent:: 查找其父节点 12345 from lxml import etreehtml = etree.parse('./xpath.html')result = html.xpath('//ol/../@class')print(result) 12345from lxml import etreehtml = etree.parse('./xpath.html')result = html.xpath('//ol/parent::*/@class')print(result) 先查找到 ol 节点,随后获取其父节点以及其 class 属性:1['tang'] 【7.6】属性匹配 有时候 HTML 包含多个相同名的节点,而节点的属性是不一样的,此时可以用 @ 符号进行属性过滤 12345 from lxml import etreehtml = etree.parse('./xpath.html')result = html.xpath('//li[@class=\"balucy\"]')print(result) xpath.html 文件中,只有一个 class 为 balucy 的节点:<li class="balucy">寻寻觅觅冷冷清清,凄凄惨惨戚戚。</li>,运行以上代码将返回一个该元素:1[<Element li at 0x16e53aa54c8>] 【7.7】文本获取 使用 text() 方法即可提取节点中的文本: 12345 from lxml import etreehtml = etree.parse('./xpath.html')result = html.xpath('//li[@class=\"balucy\"]/text()')print(result) 输出结果:1['寻寻觅觅冷冷清清,凄凄惨惨戚戚。'] 再次观察 xpath.html 文件中的 <ol></ol>这一部分:123456789<ol> <li class=\"balucy\">寻寻觅觅冷冷清清,凄凄惨惨戚戚。</li> <li class=\"lily\">咋暖还寒时候,最难将息。</li> <li class=\"lilei\">三杯两盏淡酒。</li> <li>怎敌他晚来风急。</li> <li>雁过也,正伤心,却是旧时相识。</li> <li>爱情三十六计</li> <li>什么是爱情</li></ol> 如果我们想要提取 <li> 节点里面所有的文本,就可以使用 html.xpath('//ol/li/text()') 语句:12345from lxml import etreehtml = etree.parse('./xpath.html')result = html.xpath('//ol/li/text()')print(result) 输出结果:1['寻寻觅觅冷冷清清,凄凄惨惨戚戚。', '咋暖还寒时候,最难将息。', '三杯两盏淡酒。', '怎敌他晚来风急。', '雁过也,正伤心,却是旧时相识。', '爱情三十六计', '什么是爱情'] 同样还有另一种方法,使用 html.xpath('//ol//text()') 语句,// 将会选取所有子孙节点的文本,<ol> 和 <li> 节点下的换行符也将被提取出来:12345from lxml import etreehtml = etree.parse('./xpath.html')result = html.xpath('//ol//text()')print(result) 输出结果:1['\\n ', '寻寻觅觅冷冷清清,凄凄惨惨戚戚。', '\\n ', '咋暖还寒时候,最难将息。', '\\n ', '三杯两盏淡酒。', '\\n ', '怎敌他晚来风急。', '\\n ', '雁过也,正伤心,却是旧时相识。', '\\n ', '爱情三十六计', '\\n ', '什么是爱情', '\\n '] 【7.8】属性获取 与属性匹配一样,属性获取仍然使用 @: 12345 from lxml import etreehtml = etree.parse('./xpath.html')result = html.xpath('//ul/li[5]/a/@href')print(result) 获取 href 属性: 1['http://www.baidu.com/'] 【7.9】一个属性包含多个值的匹配某个节点的某个属性可能有多个值,例如:1<li class=\"li li-first\"><a href=\"link.html\">first item</a></li> li 节点的 class 属性有 li 和 li-first 两个值,如果使用 html.xpath('//li[@class="li"] 语句,将无法成功匹配,这时就需要使用 contains 方法了,第一个参数传入属性名称,第二个参数传入属性值,只要此属性包含所传入的属性值,就可以完成匹配了 1234567from lxml import etreetext = ''' <li class=\"li li-first\"><a href=\"link.html\">first item</a></li> '''html = etree.HTML(text)result = html.xpath('//li[contains(@class, \"li\")]/a/text()')print(result) 输出结果:1['first item'] 【7.10】多个属性匹配一个节点XPath 还可以根据多个属性来确定一个节点,这时就需要同时匹配多个属性。此时可以使用运算符 and 来连接:1234567from lxml import etreetext = ''' <li class=\"li\" name=\"item\"><a href=\"link.html\">first item</a></li>'''html = etree.HTML(text)result = html.xpath('//li[@class=\"li\" and @name=\"item\"]/a/text()')print(result) 输出结果:1['first item'] 示例中运用了运算符 and 来连接,此外常见的运算符如下: 运算符 描述 实例 返回值 or 或 age=19 or age=20 如果 age 是 19 或者 20,则返回 true。如果 age 是其他值,则返回 false and 与 age>19 and age<21 如果 age 大于 19 且小于 21,则返回 true。如果 age 是其他值,则返回 false mod 计算除法的余数 5 mod 2 1 | 计算两个节点集 //book | //cd 返回所有拥有 book 和 cd 元素的节点集 + 加法 10 + 5 15 - 减法 10 - 5 5 * 乘法 10 * 5 50 div 除法 10 div 5 2 = 等于 age=19 如果 age 是 19,则返回 true。如果 age 不是 19,则返回 false != 不等于 age!=19 如果 age 不是 19,则返回 true。如果 age 是 19,则返回 false < 小于 age<19 如果 age 小于 19,则返回 true。如果 age 不小于 19,则返回 false <= 小于或等于 age<=19 如果 age 小于等于 19,则返回 true。如果 age 大于 19,则返回 false > 大于 age>19 如果 age 大于 19,则返回 true。如果 age 不大于 19,则返回 false >= 大于或等于 age>=19 如果 age 大于等于 19,则返回 true。如果 age 小于 19,则返回 false 【7.11】按顺序选择节点某些属性可能同时匹配了多个节点,如果要选择其中几个节点,可以利用中括号传入索引的方法获取特定次序的节点12345678910111213141516171819202122from lxml import etreetext = '''<div> <ul> <li class=\"item-0\"><a href=\"link1.html\">first item</a></li> <li class=\"item-1\"><a href=\"link2.html\">second item</a></li> <li class=\"item-inactive\"><a href=\"link3.html\">third item</a></li> <li class=\"item-1\"><a href=\"link4.html\">fourth item</a></li> <li class=\"item-0\"><a href=\"link5.html\">fifth item</a> </ul> </div>'''html = etree.HTML(text)result = html.xpath('//li[1]/a/text()')print(result)result = html.xpath('//li[last()]/a/text()')print(result)result = html.xpath('//li[position()<3]/a/text()')print(result)result = html.xpath('//li[last()-2]/a/text()')print(result) li[1]:选取第一个 li 节点; li[last()]:选取最后一个 li 节点; position()<3:选取位置小于 3 的 li 节点; li[last()-2]:选取倒数第三个 li 节点 输出结果:1234['first item']['fifth item']['first item', 'second item']['third item'] 【7.12】节点轴选择节点轴选择:获取子元素、兄弟元素、父元素、祖先元素等12345678910111213141516171819202122232425262728from lxml import etreetext = '''<div> <ul> <li class=\"item-0\"><a href=\"link1.html\"><span>first item</span></a></li> <li class=\"item-1\"><a href=\"link2.html\">second item</a></li> <li class=\"item-inactive\"><a href=\"link3.html\">third item</a></li> <li class=\"item-1\"><a href=\"link4.html\">fourth item</a></li> <li class=\"item-0\"><a href=\"link5.html\">fifth item</a> </ul> </div>'''html = etree.HTML(text)result = html.xpath('//li[1]/ancestor::*')print(result)result = html.xpath('//li[1]/ancestor::div')print(result)result = html.xpath('//li[1]/attribute::*')print(result)result = html.xpath('//li[1]/child::a[@href=\"link1.html\"]')print(result)result = html.xpath('//li[1]/descendant::span')print(result)result = html.xpath('//li[1]/following::*[2]')print(result)result = html.xpath('//li[1]/following-sibling::*')print(result) 输出结果:1234567[<Element html at 0x1d3749e9548>, <Element body at 0x1d3749e94c8>, <Element div at 0x1d3749e9488>, <Element ul at 0x1d3749e9588>][<Element div at 0x1d3749e9488>]['item-0'][<Element a at 0x1d3749e9588>][<Element span at 0x1d3749e9488>][<Element a at 0x1d3749e9588>][<Element li at 0x1d3749e94c8>, <Element li at 0x1d3749e95c8>, <Element li at 0x1d3749e9608>, <Element li at 0x1d3749e9648>] 基本语法:轴名称::节点测试[谓语] 轴名称对应的结果: 轴名称 结果 ancestor 选取当前节点的所有先辈(父、祖父等) ancestor-or-self 选取当前节点的所有先辈(父、祖父等)以及当前节点本身 attribute 选取当前节点的所有属性 child 选取当前节点的所有子元素 descendant 选取当前节点的所有后代元素(子、孙等) descendant-or-self 选取当前节点的所有后代元素(子、孙等)以及当前节点本身 following 选取文档中当前节点的结束标签之后的所有节点 namespace 选取当前节点的所有命名空间节点 parent 选取当前节点的父节点 preceding 选取文档中当前节点的开始标签之前的所有节点 preceding-sibling 选取当前节点之前的所有同级节点 self 选取当前节点 实例: 例子 结果 child::book 选取所有属于当前节点的子元素的 book 节点 attribute::lang 选取当前节点的 lang 属性 child::* 选取当前节点的所有子元素 attribute::* 选取当前节点的所有属性 child::text() 选取当前节点的所有文本子节点 child::node() 选取当前节点的所有子节点 descendant::book 选取当前节点的所有 book 后代 ancestor::book 选择当前节点的所有 book 先辈 ancestor-or-self::book 选取当前节点的所有 book 先辈以及当前节点(如果此节点是 book 节点) child::*/child::price 选取当前节点的所有 price 孙节点","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫学习","slug":"Python3-学习笔记/爬虫学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫学习/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"lxml","slug":"lxml","permalink":"https://www.itrhx.com/tags/lxml/"},{"name":"XPath","slug":"XPath","permalink":"https://www.itrhx.com/tags/XPath/"}]},{"title":"Python3 爬虫学习笔记 C06","slug":"A36-Python3-spider-C06","date":"2019-08-24T10:37:05.278Z","updated":"2019-09-24T12:40:18.770Z","comments":true,"path":"2019/08/24/A36-Python3-spider-C06/","link":"","permalink":"https://www.itrhx.com/2019/08/24/A36-Python3-spider-C06/","excerpt":"Python3 爬虫学习笔记第六章 —— 【正则表达式】","text":"Python3 爬虫学习笔记第六章 —— 【正则表达式】 【6.1】关于正则表达式正则表达式是对字符串操作的一种逻辑公式,用定义好的特定字符和这些特定字符的组合组成一个规则字符串,这个规则字符串原来表达对字符串的一种过滤逻辑,从而实现字符串的检索、替换、匹配验证等。Python 的 re 库提供了整个正则表达式的实现,包含五种方法:match、search、findall、sub、compile常用的匹配规则: 模式 描述 \\w 匹配字母、数字及下划线 \\W 匹配不是字母、数字及下划线的字符 \\s 匹配任意空白字符,等价于 [\\t\\n\\r\\f] \\S 匹配任意非空字符 \\d 匹配任意数字,等价于 [0-9] \\D 匹配任意非数字的字符 \\A 匹配字符串开头 \\z 匹配字符串结尾,如果存在换行,同时还会匹配换行符 \\Z 匹配字符串结尾,如果存在换行,只匹配到换行前的结束字符串 \\G 匹配最后匹配完成的位置 \\n 匹配一个换行符 \\t 匹配一个制表符 ^ 匹配一行字符串的开头 $ 匹配一行字符串的结尾 . 匹配任意字符,除了换行符,当 re.DOTALL 标记被指定时,则可以匹配包括换行符的任意字符 […] 用来表示一组字符,单独列出,比如 [amk] 匹配 a、m 或 k [^…] 不在 [] 中的字符,比如 匹配除了 a、b、c 之外的字符 * 匹配 0 个或多个表达式 + 匹配 1 个或多个表达式 ? 匹配 0 个或 1 个前面的正则表达式定义的片段,非贪婪方式 {n} 精确匹配 n 个前面的表达式 {n, m} 匹配 n 到 m 次由前面正则表达式定义的片段,贪婪方式 a\\ b 匹配 a 或 b ( ) 匹配括号内的表达式,也表示一个组 【6.2】re.match 方法match() 方法会尝试从字符串的起始位置匹配正则表达式,如果匹配,就返回匹配成功的结果;如果不匹配,就返回 None,在 match() 方法中,第一个参数传入正则表达式,第二个参数传入要匹配的字符串。12345678import recontent = 'This is a Demo_123 4567_I Love China'print(len(content))result = re.match('^This\\s\\w\\w\\s\\w\\s\\w{5}\\d{3}\\s\\w{6}', content)print(result)print(result.group())print(result.span()) 输出结果:123436<_sre.SRE_Match object; span=(0, 25), match='This is a Demo_123 4567_I'>This is a Demo_123 4567_I(0, 25) 打印 result 结果是 SRE_Match 对象,表明匹配成功。SRE_Match 对象有两种方法:group() 方法可以输出匹配到的内容;span() 方法可以输出匹配的范围。 【6.2.1】提取内容使用括号将想提取的子字符串括起来。括号实际上标记了一个子表达式的开始和结束位置,被标记的每个子表达式会依次对应每一个分组,调用 group() 方法传入分组的索引即可获取提取的结果。12345678import recontent = 'This is a Demo_123 4567_I Love China'result = re.match('^This\\s\\w\\w\\s\\w\\s(\\w{5})\\d{3}\\s\\w{6}', content)print(result)print(result.group())print(result.group(1))print(result.span()) 输出结果:1234<_sre.SRE_Match object; span=(0, 25), match='This is a Demo_123 4567_I'>This is a Demo_123 4567_IDemo_(0, 25) 【6.2.2】通用匹配如果每个字符都用都用一个符号来匹配的话就显得比较麻烦,可以用 .*来匹配,. 可以匹配除换行符外的任意字符,* 代表匹配前面的字符无限次。1234567import recontent = 'This is a Demo_123 4567_I Love China'result = re.match('^This.*China$', content)print(result)print(result.group())print(result.span()) 输出结果:123<_sre.SRE_Match object; span=(0, 36), match='This is a Demo_123 4567_I Love China'>This is a Demo_123 4567_I Love China(0, 36) 【6.2.3】贪婪匹配12345678import recontent = 'This is a Demo_1234567_I Love China'result = re.match('^This.*(\\d+).*China$', content)print(result)print(result.group())print(result.group(1))print(result.span()) 输出结果:1234<_sre.SRE_Match object; span=(0, 35), match='This is a Demo_1234567_I Love China'>This is a Demo_1234567_I Love China7(0, 35) .* 为贪婪匹配,会匹配尽可能多的字符,所以 \\d+ 只会匹配到最后一个数字,而不是所有的数字 【6.2.4】非贪婪匹配12345678import recontent = 'This is a Demo_1234567_I Love China'result = re.match('^This.*?(\\d+).*China$', content)print(result)print(result.group())print(result.group(1))print(result.span()) 输出结果:1234<_sre.SRE_Match object; span=(0, 35), match='This is a Demo_1234567_I Love China'>This is a Demo_1234567_I Love China1234567(0, 35) .*? 为非贪婪匹配,会匹配尽可能少的字符,所以 \\d+ 会匹配到所有的数字 【6.2.5】转义匹配当遇到用于正则匹配模式的特殊字符时,在前面加反斜线转义一下即可。例如 . 可以用 \\. 来匹配:123456import recontent = '(博客)www.itrhx.com'result = re.match('\\(博客\\)www\\.itrhx\\.com', content)print(result)print(result.group()) 输出结果:12<_sre.SRE_Match object; span=(0, 17), match='(博客)www.itrhx.com'>(博客)www.itrhx.com 【6.2.6】修饰符修饰符用来解决换行、大小写等问题,较为常用的有 re.S 和 re.I。 修饰符 描述 re.S 使 . 匹配包括换行在内的所有字符 re.I 使匹配对大小写不敏感 re.L 做本地化识别(locale-aware)匹配 re.M 多行匹配,影响 ^ 和 $ re.U 根据 Unicode 字符集解析字符。这个标志影响 \\w、\\W、\\b 和 \\B re.X 该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解 示例:123456789import recontent = '''This is a Demo_1234567 _I Love China'''result = re.match('^This.*?(\\d+).*China$', content)print(result)print(result.group())print(result.group(1))print(result.span()) 示例中 content 字段进行了换行处理,如果没有修饰符,就会报错:12345Traceback (most recent call last):None File \"F:/PycharmProjects/Python3爬虫/test.py\", line 7, in <module> print(result.group())AttributeError: 'NoneType' object has no attribute 'group' 添加 re.S 修饰符后即可匹配成功:123456789import recontent = '''This is a Demo_1234567 _I Love China'''result = re.match('^This.*?(\\d+).*China$', content, re.S)print(result)print(result.group())print(result.group(1))print(result.span()) 输出结果:12345<_sre.SRE_Match object; span=(0, 46), match='This is a Demo_1234567\\n _I Love China'>This is a Demo_1234567 _I Love China1234567(0, 46) 【6.3】re.search 方法match() 方法只能从字符串的开头开始匹配,一旦开头不匹配,那么整个匹配就失败了,match() 方法更适合用来检测某个字符串是否符合某个正则表达式的规则,而 search() 方法则会扫描整个字符串并返回第一个成功的匹配123456import recontent = 'This is a Demo_1234567_I Love China'result = re.search('a.*?(\\d{5})', content)print(result)print(result.group(1)) 输出结果:12<_sre.SRE_Match object; span=(8, 20), match='a Demo_12345'>12345 【6.4】re.findall 方法search() 方法则会扫描整个字符串,但是返回的是第一个成功的匹配,而 findall() 方法将会返回所有成功的匹配12345678910111213141516171819202122232425262728import rehtml = '''<div id=\"songs-list\"> <h2 class=\"title\"> 民谣 </h2> <p class=\"introduction\"> 民谣歌曲列表 </p> <ul id=\"list\" class=\"list-group\"> <li data-view=\"2\"> 七里香 </li> <li data-view=\"7\"> <a href=\"/2.mp3\" singer=\"赵雷\"> 理想 </a> </li> <li data-view=\"4\" class=\"active\"> <a href=\"/3.mp3\" singer=\"许巍\"> 像风一样自由 </a> </li> <li data-view=\"6\"><a href=\"/4.mp3\" singer=\"安与骑兵\"> 红山果 </a></li> <li data-view=\"5\"><a href=\"/5.mp3\" singer=\"薛之谦\"> 意外 </a></li> <li data-view=\"5\"> <a href=\"/6.mp3\" singer=\"马頔\"> 但南山南 </a> </li> </ul> </div>'''results = re.findall('<li.*?href=\"(.*?)\".*?singer=\"(.*?)\">(.*?)</a>', html, re.S)print(results)print(type(results))for result in results: print(result) print(result[0], result[1], result[2]) 输出结果:123456789101112[('/2.mp3', '赵雷', ' 理想 '), ('/3.mp3', '许巍', ' 像风一样自由 '), ('/4.mp3', '安与骑兵', ' 红山果 '), ('/5.mp3', '薛之谦', ' 意外 '), ('/6.mp3', '马頔', ' 但南山南 ')]<class 'list'>('/2.mp3', '赵雷', ' 理想 ')/2.mp3 赵雷 理想 ('/3.mp3', '许巍', ' 像风一样自由 ')/3.mp3 许巍 像风一样自由 ('/4.mp3', '安与骑兵', ' 红山果 ')/4.mp3 安与骑兵 红山果 ('/5.mp3', '薛之谦', ' 意外 ')/5.mp3 薛之谦 意外 ('/6.mp3', '马頔', ' 但南山南 ')/6.mp3 马頔 但南山南 【6.5】re.sub 方法与字符串的 replace() 方法类似,sub() 方法可以对文本进行修改,sub() 方法第一个参数为匹配对象,第二个参数为替换成的字符串,如果要去掉匹配对象的话,可以赋值为空,第三个参数为原来的字符串12345import recontent = '87dsf4as2w4jh1k4kdl4'result = re.sub('\\d+', '', content)print(result) 输出结果:1dsfaswjhkkdl 【6.5】re.compile() 方法compile() 方法可以将正则字符串编译成正则表达式对象,以便在后面的匹配中复用123456789101112import recontent1 = '北京时间:2019-08-24 18:30'content2 = '伦敦时间:2019-08-24 11:30'content3 = '巴黎时间:2019-08-24 12:30'content4 = '外星时间:9019-99-66 50:30'pattern = re.compile('\\d{2}:\\d{2}')result1 = re.sub(pattern, '', content1)result2 = re.sub(pattern, '', content2)result3 = re.sub(pattern, '', content3)result4 = re.sub(pattern, '', content4)print(result1, result2, result3, result4) 利用 compile() 方法将正则表达式编译成一个正则表达式对象,以便复用,然后用 sub() 方法去掉具体时间输出结果:1北京时间:2019-08-24 伦敦时间:2019-08-24 巴黎时间:2019-08-24 外星时间:9019-99-66","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫学习","slug":"Python3-学习笔记/爬虫学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫学习/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"正则表达式","slug":"正则表达式","permalink":"https://www.itrhx.com/tags/正则表达式/"}]},{"title":"Python3 爬虫学习笔记 C05","slug":"A35-Python3-spider-C05","date":"2019-08-23T12:13:55.085Z","updated":"2019-09-24T12:40:15.920Z","comments":true,"path":"2019/08/23/A35-Python3-spider-C05/","link":"","permalink":"https://www.itrhx.com/2019/08/23/A35-Python3-spider-C05/","excerpt":"Python3 爬虫学习笔记第五章 —— 【Selenium + 无界面浏览器】","text":"Python3 爬虫学习笔记第五章 —— 【Selenium + 无界面浏览器】 【5.1】关于无界面浏览器无界面(headless)浏览器,会把网站加载到内存并执行页面上的 JavaScript,因为不会展示图形界面,所以运行起来比完整的浏览器更高效。Selenium 搭配无界面浏览器使用,被称为爬虫利器,常用的无界面浏览器有:PhantomJS、Headless Chrome、Headless Firefox,其中,18年3月,PhantomJS 的作者在 GitHub 上宣布暂停开发 PhantomJS,现在使用 PhantomJS 会出现警告:UserWarning: Selenium support for PhantomJS has been deprecated, please use headless versions of Chrome or Firefox instead,所以推荐使用谷歌或者火狐的无界面浏览器 【5.2】PhantomJS下载 PhantomJS:https://phantomjs.org/download.htmlpath 为 PhantomJS 路径,如果系统配置了环境变量,就不用手动指定 executable_path 参数1234567from selenium import webdriverpath = r'F:\\PycharmProjects\\Python3爬虫\\phantomjs-2.1.1\\bin\\phantomjs.exe'driver = webdriver.PhantomJS(executable_path=path)driver.get(\"https://www.itrhx.com\")print(driver.page_source)driver.close() 【5.3】Headless Chrome下载 Chromedriver:http://chromedriver.storage.googleapis.com/index.html需要本地有 Chrome 浏览器,path 为 Headless Chrome 路径,如果系统配置了环境变量,就不用手动指定 executable_path 参数1234567891011from selenium import webdriverfrom selenium.webdriver.chrome.options import Optionschrome_options = Options()chrome_options.add_argument('--headless')chrome_options.add_argument('--disable-gpu')path = 'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe'driver = webdriver.Chrome(executable_path=path, chrome_options=chrome_options)driver.get(\"https://www.itrhx.com\")print(driver.page_source)driver.close() 【5.4】Headless Firefox下载 geckodriver:https://github.com/mozilla/geckodriver/releases/需要本地有 Firefox 浏览器,path 为 Headless Firefox 路径,如果系统配置了环境变量,就不用手动指定 executable_path 参数12345678910from selenium.webdriver import Firefoxfrom selenium.webdriver.firefox.options import Optionsoptions = Options()options.add_argument('-headless')path = 'F:\\PycharmProjects\\Python3爬虫\\geckodriver.exe'driver = Firefox(executable_path=path, firefox_options=options)driver.get(\"https://www.itrhx.com\")print(driver.page_source)driver.close()","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫学习","slug":"Python3-学习笔记/爬虫学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫学习/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"Selenium","slug":"Selenium","permalink":"https://www.itrhx.com/tags/Selenium/"},{"name":"无界面浏览器","slug":"无界面浏览器","permalink":"https://www.itrhx.com/tags/无界面浏览器/"}]},{"title":"常见 User-Agent 大全","slug":"A34-UserAgent","date":"2019-08-23T01:28:22.624Z","updated":"2019-09-24T12:47:21.096Z","comments":true,"path":"2019/08/23/A34-UserAgent/","link":"","permalink":"https://www.itrhx.com/2019/08/23/A34-UserAgent/","excerpt":"","text":"User Agent 中文名为用户代理,简称 UA,是一个特殊字符串头,使得服务器能够识别客户使用的操作系统及版本、CPU 类型、浏览器及版本、浏览器渲染引擎、浏览器语言、浏览器插件等。Python 爬虫通过伪装 UA 可以绕过某些检测。 以下为搜集的常见的各浏览器的 User-Agent,其中: 安卓操作系统:Android 7.1.1;OPPO R9sk Build/NMF26F PC操作系统:Windows 10 64位 10.0.18362.10000 其他操作系统:iOS、Backerry、WebOS、Symbian、Windows Phone 相关链接: 手机User-Agent大全:http://www.fynas.com/ua User-Agent在线检测:http://www.user-agent.cn/ 常用User-Agent大全:http://www.jsons.cn/useragent/ Windows10 Windows10 / Chrome 75.0.3770.142Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36 Windows10 / Firefox 69.0b15Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:69.0) Gecko/20100101 Firefox/69.0 Windows10 / Opera 63.0.3368.43Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36 OPR/63.0.3368.43 Windows10 / Edge 44.18362.1.0User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.18362 Windows10 / IE 11.10000.18362.0User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; LCTE; rv:11.0) like Gecko Windows10 x64 / Safari 5.1.4(7534.54.16)Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/534.54.16 (KHTML, like Gecko) Version/5.1.4 Safari/534.54.16 Windows10 / QQ浏览器 10.5(3739)Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3722.400 QQBrowser/10.5.3739.400 Windows10 / 360安全浏览器 10.0.1977.0Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36 QIHU 360SE Windows10 / 360极速浏览器 11.0.2179.0Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36 QIHU 360EE Windows10 / UC浏览器 6.2.3964.2Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 UBrowser/6.2.3964.2 Safari/537.36 Windows10 / 搜狗浏览器 8.5.10.31270Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 SE 2.X MetaSr 1.0 Windows10 / 猎豹浏览器 6.5.115.19331.8001Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36 LBBROWSER Windows10 / 傲游浏览器 5.2.7.5000Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36 Windows10 / 2345加速浏览器 10.1.0.19399Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3947.100 Safari/537.36 Android Android / Chrome 76.0.3809.111Mozilla/5.0 (Linux; Android 7.1.1; OPPO R9sk) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.111 Mobile Safari/537.36 Android / Firefox 68.0.2Mozilla/5.0 (Android 7.1.1; Mobile; rv:68.0) Gecko/68.0 Firefox/68.0 Android / Opera 53.0.2569.141117Mozilla/5.0 (Linux; Android 7.1.1; OPPO R9sk Build/NMF26F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Mobile Safari/537.36 OPR/53.0.2569.141117 Android / Edge 42.0.2.3819Mozilla/5.0 (Linux; Android 7.1.1; OPPO R9sk) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.90 Mobile Safari/537.36 EdgA/42.0.2.3819 Android / QQ浏览器 9.6.1.5190Mozilla/5.0 (Linux; U; Android 7.1.1; zh-cn; OPPO R9sk Build/NMF26F) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.126 MQQBrowser/9.6 Mobile Safari/537.36 Android / OPPO浏览器 10.5.1.2_2c91537Mozilla/5.0 (Linux; U; Android 7.1.1; zh-cn; OPPO R9sk Build/NMF26F) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.80 Mobile Safari/537.36 OppoBrowser/10.5.1.2 Android / 360浏览器 8.2.0.162Mozilla/5.0 (Linux; Android 7.1.1; OPPO R9sk Build/NMF26F; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/62.0.3202.97 Mobile Safari/537.36 Android / 360极速浏览器 1.0.100.1078Mozilla/5.0 (Linux; Android 7.1.1; OPPO R9sk Build/NMF26F) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.80 Mobile Safari/537.36 360 Alitephone Browser (1.5.0.90/1.0.100.1078) mso_sdk(1.0.0) Android / UC浏览器 12.6.0.1040Mozilla/5.0 (Linux; U; Android 7.1.1; zh-CN; OPPO R9sk Build/NMF26F) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/12.6.0.1040 Mobile Safari/537.36 Android / 猎豹浏览器 5.12.3Mozilla/5.0 (Linux; Android 7.1.1; OPPO R9sk Build/NMF26F; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.80 Mobile Safari/537.36 LieBaoFast/5.12.3 Android / 百度浏览器 7.19Mozilla/5.0 (Linux; Android 7.1.1; OPPO R9sk Build/NMF26F; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/48.0.2564.116 Mobile Safari/537.36 T7/9.1 baidubrowser/7.19.13.0 (Baidu; P1 7.1.1) Android / 搜狗浏览器 5.22.8.71677Mozilla/5.0 (Linux; Android 7.1.1; OPPO R9sk Build/NMF26F; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/68.0.3440.106 Mobile Safari/537.36 AWP/2.0 SogouMSE,SogouMobileBrowser/5.22.8 Android / 2345浏览器 11.0.1Mozilla/5.0 (Linux; Android 7.1.1; OPPO R9sk Build/NMF26F; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.80 Mobile Safari/537.36 Mb2345Browser/11.0.1 其他 iPhone3Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us) AppleWebKit/420.1 (KHTML, like Gecko) Version/3.0 Mobile/1A542a Safari/419.3 iPhone4Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_0 like Mac OS X; en-us) AppleWebKit/532.9 (KHTML, like Gecko) Version/4.0.5 Mobile/8A293 Safari/6531.22.7 iPhone6sMozilla/5.0 (iPhone 6s; CPU iPhone OS 11_4_1 like Mac OS X) AppleWebKit/604.3.5 (KHTML, like Gecko) Version/11.0 MQQBrowser/8.3.0 Mobile/15B87 Safari/604.1 MttCustomUA/2 QBWebViewType/1 WKType/1 iPadMozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B334b Safari/531.21.10 iPodMozilla/5.0 (iPod; U; CPU iPhone OS 4_3_3 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5 BlackBerryMozilla/5.0 (BlackBerry; U; BlackBerry 9800; en) AppleWebKit/534.1+ (KHTML, like Gecko) Version/6.0.0.337 Mobile Safari/534.1+ WebOS HP TouchpadMozilla/5.0 (hp-tablet; Linux; hpwOS/3.0.0; U; en-US) AppleWebKit/534.6 (KHTML, like Gecko) wOSBrowser/233.70 Safari/534.6 TouchPad/1.0 Nokia N97Mozilla/5.0 (SymbianOS/9.4; Series60/5.0 NokiaN97-1/20.0.019; Profile/MIDP-2.1 Configuration/CLDC-1.1) AppleWebKit/525 (KHTML, like Gecko) BrowserNG/7.1.18124 Windows Phone MangoMozilla/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident/5.0; IEMobile/9.0; HTC; Titan)","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"学习经验","slug":"Python3-学习笔记/学习经验","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/学习经验/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"User-Agent","slug":"User-Agent","permalink":"https://www.itrhx.com/tags/User-Agent/"}]},{"title":"Selenium 显式等待条件及其含义","slug":"A33-selenium","date":"2019-08-23T01:28:22.478Z","updated":"2019-09-24T12:47:18.475Z","comments":true,"path":"2019/08/23/A33-selenium/","link":"","permalink":"https://www.itrhx.com/2019/08/23/A33-selenium/","excerpt":"","text":"等待条件 含义 title_is 标题是某内容 title_contains 标题包含某内容 presence_of_element_located 节点加载出,传入定位元组,如 (By.ID, ‘p’) visibility_of_element_located 节点可见,传入定位元组 visibility_of 可见,传入节点对象 presence_of_all_elements_located 所有节点加载出 text_to_be_present_in_element 某个节点文本包含某文字 text_to_be_present_in_element_value 某个节点值包含某文字 frame_to_be_available_and_switch_to_it frame 加载并切换 invisibility_of_element_located 节点不可见 element_to_be_clickable 节点可点击 staleness_of 判断一个节点是否仍在 DOM,可判断页面是否已经刷新 element_to_be_selected 节点可选择,传节点对象 element_located_to_be_selected 节点可选择,传入定位元组 element_selection_state_to_be 传入节点对象以及状态,相等返回 True,否则返回 False element_located_selection_state_to_be 传入定位元组以及状态,相等返回 True,否则返回 False alert_is_present 是否出现 Alert 更多等待条件极其用法介绍:https://selenium-python.readthedocs.io/api.html#module-selenium.webdriver.support.expected_conditions Selenium 的使用:https://www.itrhx.com/2019/08/22/A32-Python3-spider-C04/","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"学习经验","slug":"Python3-学习笔记/学习经验","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/学习经验/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"Selenium","slug":"Selenium","permalink":"https://www.itrhx.com/tags/Selenium/"}]},{"title":"Python3 爬虫学习笔记 C04","slug":"A32-Python3-spider-C04","date":"2019-08-23T01:28:22.327Z","updated":"2019-09-24T12:43:57.196Z","comments":true,"path":"2019/08/23/A32-Python3-spider-C04/","link":"","permalink":"https://www.itrhx.com/2019/08/23/A32-Python3-spider-C04/","excerpt":"Python3 爬虫学习笔记第四章 —— 【自动化测试工具 Selenium】","text":"Python3 爬虫学习笔记第四章 —— 【自动化测试工具 Selenium】 Selenium 是一个用于 Web 应用程序测试的工具。Selenium 测试直接运行在浏览器中,就像真正的用户在操作一样。支持的浏览器包括IE(7, 8, 9, 10, 11),Mozilla Firefox,Safari,Google Chrome,Opera等。利用它可以驱动浏览器执行特定的动作,如点击、下拉等操作,同时还可以获取浏览器当前呈现的页面的源代码,做到可见即可爬。对于一些 JavaScript 动态渲染的页面来说,此种抓取方式非常有效。本文重点以 Selenium 使用谷歌浏览器的 Webdriver 为例。 【4.1】下载驱动使用 Selenium 操作不同浏览器,需要不同浏览器相应的驱动支持: 浏览器 驱动名称 下载地址 备注 谷歌浏览器 chromedriver 点击进入下载页面 需要根据自己浏览器的版本下载不同版本的驱动 火狐浏览器 geckodriver 点击进入下载页面 需要根据自己的操作系统下载对应的驱动 IE IEDriverServer 点击进入下载页面 根据自己 selenium 版本和系统版本下载对应版本的驱动, selenium 版本可以在cmd中输入pip show selenium查看 【4.2】声明浏览器对象不同浏览器的对象声明方法:1234567from selenium import webdriverbrowser = webdriver.Chrome() # 谷歌浏览器browser = webdriver.Firefox() # 火狐浏览器browser = webdriver.Edge() # Edgebrowser = webdriver.PhantomJS() # PhantomJS无界面浏览器browser = webdriver.Safari() # Safari浏览器 【4.3】访问页面1234567from selenium import webdriverpath = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe'browser = webdriver.Chrome(executable_path=path)browser.get('https://www.itrhx.com')print(browser.page_source)browser.close() 运行代码就会自动打开谷歌浏览器,实现了用 get() 方法访问 www.itrhx.com ,path 里面的内容是谷歌浏览器驱动的目录, r 表示不转义,使用真实字符。print(browser.page_source) 表示打印页面源代码 【4.4】启动参数Chrome Options 是一个 Chrome 的参数对象,在此对象中使用 add_argument() 方法可以添加启动参数,添加完毕后可以在初始化 Webdriver 对象时将此 Options 对象传入,则可以实现以特定参数启动Chrome。示例:123456789101112from selenium import webdriverfrom selenium.webdriver.chrome.options import Optionspath = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe'# 实例化一个启动参数对象chrome_options = Options()# 添加启动参数chrome_options.add_argument('--window-size=1366,768')# 将参数对象传入Chrome,则启动了一个设置了窗口大小的Chromebrowser = webdriver.Chrome(executable_path=path, chrome_options=chrome_options)browser.get('http://www.itrhx.com') 这样就启动了一个1366x768分辨率的浏览器常见的启动参数: 启动参数 作用 –user-agent=”” 设置请求头的 User-Agent –window-size=xxx, xxx 设置浏览器分辨率 –headless 无界面运行 –start-maximized 最大化运行 –incognito 隐身模式 –disable-javascript 禁用javascript –disable-infobars 禁用“浏览器正在被自动化程序控制”的提示 所有的启动参数:https://peter.sh/experiments/chromium-command-line-switches/ 【4.5】查找节点Selenium 可以驱动浏览器完成各种操作,比如填充表单、模拟点击等。要完成这些操作,实现要知道在哪里点击,哪里填充,这就是 Selenium 节点查找 【4.5.1】查找单个节点所有获取单个节点的方法: find_element_by_id 【通过元素的 id 来选择】例:<div id='bdy-inner'>test</div>,查找:driver.find_element_by_id('bdy-inner') find_element_by_name 【通过元素的 name 来选择】例:<input name="username" type="text" />,查找:driver.find_element_by_name('password') find_element_by_xpath 【通过 xpath 选择】例:<form id="loginForm">,查找:driver.find_element_by_xpath("//form[@id='loginForm']") find_element_by_link_text 【通过链接地址选择】例:<a href="continue.html">continue</a>,查询:driver.find_element_by_link_text('continue') find_element_by_partial_link_text 【通过链接的部分地址选择】例:<a href="continue.html">continue</a>,查询:driver.find_element_by_link_text('cont') find_element_by_tag_name 【通过元素的名称选择】例:<h1>welcome<h1>,查询:driver.find_element_by_tag_name('h1') find_element_by_class_name 【通过元素的 class 选择】例:<p class="content">welcome to TRHX'S BLOG!</p>,查询:driver.find_element_by_class_name('content') find_element_by_css_selector 【通过元素的 class 选择】例:<div class='bdy-inner'>test</div>,查询:driver.find_element_by_css_selector('div.bdy-inner') find_element() 【通用方法,需要传递两个参数:查找方式 By 和值】例:driver.find_element_by_id('inner') 等价于 find_element(By.ID, inner),使用时需要from selenium.webdriver.common.by import By 示例:12345678from selenium import webdriverpath = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe'browser = webdriver.Chrome(executable_path=path)browser.get('https://www.itrhx.com')blog_title = browser.find_elements_by_class_name(('title'))print(blog_title[0].text)browser.close() 输出结果:1TRHX'S BLOG 【4.5.2】查找多个节点所有获取多个节点的方法:(与查找单个节点的区别是 element 多加了个 s) find_elements_by_id find_elements_by_name find_elements_by_xpath find_elements_by_link_text find_elements_by_partial_link_text find_elements_by_tag_name find_elements_by_class_name find_elements_by_css_selector find_elements() 示例:123456789from selenium import webdriverfrom selenium.webdriver.common.by import Bypath = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe'browser = webdriver.Chrome(executable_path=path)browser.get('https://www.itrhx.com')article_title = browser.find_elements(By.XPATH, \"//h2[@class='title']\")print(article_title)browser.close() 【4.6】节点交互Selenium 可以驱动浏览器来执行一些操作,也就是说可以让浏览器模拟执行一些动作。称为节点交互,比较常见的用法有: send_keys:模拟按键输入 clear:清除元素的内容 click:单击元素 submit:提交表单 示例:123456789from selenium import webdriverfrom selenium.webdriver.common.keys import Keyspath = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe'browser = webdriver.Chrome(executable_path=path)browser.get('https://www.itrhx.com')search = browser.find_element_by_xpath('//div[@class=\"cover-wrapper\"]/cover/div/form/input')search.send_keys(\"Python\")search.send_keys(Keys.ENTER) 此处模拟了键盘,需要导入键盘类 Keys(),send_keys(Keys.ENTER)表示模拟回车键,程序首先打开 www.itrhx.com ,也就是我的博客,然后通过 xpath 找到搜索框,输入 Python 并回车,等待结果显示出来更多节点交互动作:https://selenium-python.readthedocs.io/api.html#module-selenium.webdriver.remote.webelement 【4.7】动作链Selenium 还有另外一些操作,它们没有特定的执行对象,比如鼠标拖曳、键盘按键等,这些动作用另一种方式来执行,那就是动作链。以一个拖曳实例为例:http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable12345678910111213from selenium import webdriverfrom selenium.webdriver import ActionChainspath = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe'browser = webdriver.Chrome(executable_path=path)url = 'http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable'browser.get(url)browser.switch_to.frame('iframeResult')source = browser.find_element_by_css_selector('#draggable')target = browser.find_element_by_css_selector('#droppable')actions = ActionChains(browser)actions.drag_and_drop(source, target)actions.perform() 依次选中要拖曳的节点和拖曳到的目标节点,接着声明 ActionChains 对象并将其赋值为 actions 变量,然后通过调用 actions 变量的 drag_and_drop() 方法,再调用 perform() 方法执行动作,此时就完成了拖曳操作,更多动作链操作:https://selenium-python.readthedocs.io/api.html#module-selenium.webdriver.common.action_chains 【4.8】执行 JavaScriptSelenium API 并没有提供执行 JavaScript 的方法,但是实际上是可以实现的。比如,下拉进度条,它可以直接模拟运行 JavaScript,此时使用 execute_script() 方法即可实现示例:1234567from selenium import webdriverpath = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe'browser = webdriver.Chrome(executable_path=path)browser.get('https://www.itrhx.com')browser.execute_script('window.scrollTo(0, document.body.scrollHeight)')browser.execute_script('alert(\"已到达最底端!\")') 以上代码实现了利用 execute_script() 方法将进度条下拉到最底部,然后弹出 alert 提示框。 【4.9】禁用加载使用Selenium 时,限制图片和 Javascript 执行,从而提高网页加载速度。123456789101112131415from selenium import webdriverpath = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe'options = webdriver.ChromeOptions()prefs = { 'profile.default_content_setting_values': { 'images': 2, 'notifications' : 2, # 禁用弹窗 'javascript': 2 # 2即为禁用的意思 }}options.add_experimental_option('prefs', prefs)browser = webdriver.Chrome(executable_path=path, chrome_options=options)browser.get('http://www.itrhx.com') 【4.10】获取节点信息通过 page_source 属性可以获取网页的源代码,然后可以使用解析库(如正则表达式、Beautiful Soup等)来提取相关信息,Selenium 已经提供了选择节点的方法,返回的是 WebElement 类型,它也有相关的方法和属性来直接提取节点信息,如属性、文本等。就不需要再次使用解析库来提取信息了 【4.10.1】获取属性使用 get_attribute() 方法来获取节点的属性:123456789from selenium import webdriverpath = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe'browser = webdriver.Chrome(executable_path=path)url = 'http://www.itrhx.com'browser.get(url)meta = browser.find_element_by_id('header-meta')print(meta)print(meta.get_attribute('class')) 输出结果:12<selenium.webdriver.remote.webelement.WebElement (session=\"d03cdaa497441d2e2a5161139b4a7ea5\", element=\"83f8fff9-60d7-4e9a-ade3-a8e97c9f0844\")>meta 【4.10.2】获取文本值每个 WebElement 节点都有 text 属性,直接调用这个属性就可以得到节点内部的文本信息,相当于 Beautiful Soup 的 get_text() 方法、pyquery 的 text() 方法示例:12345678from selenium import webdriverpath = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe'browser = webdriver.Chrome(executable_path=path)url = 'http://www.itrhx.com'browser.get(url)footer_info = browser.find_element_by_id('footer')print(footer_info.text) 输出结果:123Copyright 2018-2019 TRHX'BLOG | 鄂ICP备19003281号-4 | 本站已勉强存活了 376 天 20 小时 57 分 52 秒 | 站点地图 | 站长统计PoweredHexo HostedGitHub DNRAliyun CDNjsDelivr ThemeMaterial X BY-NC-SA 4.0 Link996.ICU UV4898 PV22066 WordCount54.9k 【4.10.3】获取 ID、位置、标签名、大小其他属性,比如 id 属性可以获取节点 id,location 属性可以获取该节点在页面中的相对位置,tag_name 属性可以获取标签名称,size 属性可以获取节点的大小等示例:1234567891011from selenium import webdriverpath = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe'browser = webdriver.Chrome(executable_path=path)url = 'http://www.itrhx.com'browser.get(url)readmore = browser.find_element_by_class_name('readmore')print(readmore.id)print(readmore.location)print(readmore.tag_name)print(readmore.size) 输出结果:12347df561d3-7ea4-4b90-96aa-64044060bb47{'x': 50, 'y': 1063}div{'height': 39, 'width': 465} 【4.11】延时等待在 Selenium 中,get() 方法会在网页框架加载结束后结束执行,某些页面有额外的 Ajax 请求,若此时立即获取 page_source,可能并不是浏览器完全加载完成的页面,这里需要延时等待一定时间,确保节点已经加载出来 【4.11.1】隐式等待当查找节点的时候,节点并没有立即出现,隐式等待将等待一段时间再查找该节点,使用 implicitly_wait() 方法可以实现隐式等待12345678from selenium import webdriverpath = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe'browser = webdriver.Chrome(executable_path=path)browser.implicitly_wait(10)browser.get('https://www.itrhx.com')readmore = browser.find_element_by_class_name('readmore')print(readmore) 【4.11.2】显式等待指定要查找的节点,然后指定一个最长等待时间。如果在规定时间内加载出来了这个节点,就立即返回查找的节点,果到了规定时间依然没有加载出该节点,则抛出超时异常123456789101112from selenium import webdriverfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECpath = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe'browser = webdriver.Chrome(executable_path=path)browser.implicitly_wait(10)browser.get('https://www.itrhx.com')wait = WebDriverWait(browser, 10)footer_info = wait.until(EC.presence_of_element_located((By.ID, 'footer')))print(footer_info) 引入 WebDriverWait 对象,指定最长等待时间,调用它的 until() 方法,传入要等待条件 expected_conditions。比如,这里传入了 presence_of_element_located 这个条件,代表节点出现的意思,其参数是节点的定位元组,也就是 ID 为 footer 的节点。 这样可以做到的效果就是,在 10 秒内如果 ID 为 footer 的节点成功加载出来,就返回该节点;如果超过 10 秒还没有加载出来,就抛出异常。 加载成功时输出结果:1<selenium.webdriver.remote.webelement.WebElement (session=\"4ca7015891fded627ab680d9462e9361\", element=\"3a80235c-9824-420b-b827-662638422765\")> 加载失败时输出结果:12345TimeoutException Traceback (most recent call last)<ipython-input-4-f3d73973b223> in <module>() 7 browser.get('https://www.itrhx.com') 8 wait = WebDriverWait(browser, 10)----> 9 input = wait.until(EC.presence_of_element_located((By.ID, 'footer'))) 【4.12】Cookies使用 Selenium,可以方便地对 Cookies 进行获取、添加、删除等操作:12345678910from selenium import webdriverpath = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe'browser = webdriver.Chrome(executable_path=path)browser.get('https://www.zhihu.com/explore')print(browser.get_cookies())browser.add_cookie({'name': 'TRHX', 'domain': 'www.zhihu.com', 'value': 'germey'})print(browser.get_cookies())browser.delete_all_cookies()print(browser.get_cookies()) 访问知乎,加载完成后,浏览器已经生成了 Cookies。调用 get_cookies() 方法获取所有的 Cookies。然后再添加一个 Cookie,传入一个字典,有 name、domain 和 value 等内容。接下来,再次获取所有的 Cookies。可以发现,结果就多了这一项新加的 Cookie。最后,调用 delete_all_cookies() 方法删除所有的 Cookies。再重新获取,发现结果就为空了输出结果:123[{'domain': 'zhihu.com', 'expiry': 1661065738.754333, 'httpOnly': False, 'name': 'd_c0', 'path': '/', 'secure': False, 'value': '\"AODi_Lod7g-PTrrXUgXb1N4MkbStCrbNlD4=|1566457741\"'}, {'domain': 'zhihu.com', 'httpOnly': False, 'name': '_xsrf', 'path': '/', 'secure': False, 'value': 'aba68431-9daf-4b62-a67a-023c1a24f0e8'}, {'domain': 'zhihu.com', 'expiry': 1629529738.75427, 'httpOnly': False, 'name': '_zap', 'path': '/', 'secure': False, 'value': 'b6f63cfc-a525-4ae6-a7bf-6384bd1e0548'}, {'domain': 'www.zhihu.com', 'expiry': 1566458637.754178, 'httpOnly': False, 'name': 'tgw_l7_route', 'path': '/', 'secure': False, 'value': '116a747939468d99065d12a386ab1c5f'}][{'domain': 'www.zhihu.com', 'httpOnly': False, 'name': 'TRHX', 'path': '/', 'secure': True, 'value': 'germey'}, {'domain': 'zhihu.com', 'expiry': 1661065738.754333, 'httpOnly': False, 'name': 'd_c0', 'path': '/', 'secure': False, 'value': '\"AODi_Lod7g-PTrrXUgXb1N4MkbStCrbNlD4=|1566457741\"'}, {'domain': 'zhihu.com', 'httpOnly': False, 'name': '_xsrf', 'path': '/', 'secure': False, 'value': 'aba68431-9daf-4b62-a67a-023c1a24f0e8'}, {'domain': 'zhihu.com', 'expiry': 1629529738.75427, 'httpOnly': False, 'name': '_zap', 'path': '/', 'secure': False, 'value': 'b6f63cfc-a525-4ae6-a7bf-6384bd1e0548'}, {'domain': 'www.zhihu.com', 'expiry': 1566458637.754178, 'httpOnly': False, 'name': 'tgw_l7_route', 'path': '/', 'secure': False, 'value': '116a747939468d99065d12a386ab1c5f'}][{'domain': 'zhihu.com', 'expiry': 1644217741.489889, 'httpOnly': False, 'name': '_xsrf', 'path': '/', 'secure': False, 'value': 'WNOjpDbNmz36B4nG1lzSAuPdTyORMX6J'}] 【4.13】前进与后退使用 back() 方法后退,使用 forward() 方法前进,与浏览器的前进后退一样示例:123456789101112from selenium import webdriverimport timepath = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe'browser = webdriver.Chrome(executable_path=path)browser.get('https://www.itrhx.com/')browser.get('https://www.baidu.com/')browser.get('https://www.zhihu.com/')browser.back()time.sleep(1)browser.forward()browser.close() 【4.14】选项卡和浏览器一样,在 Selenium 中也可以新建一个选项卡12345678910111213from selenium import webdriverimport timepath = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe'browser = webdriver.Chrome(executable_path=path)browser.get('https://www.itrhx.com')browser.execute_script('window.open()')print(browser.window_handles)browser.switch_to.window(browser.window_handles[1])browser.get('https://www.baidu.com')time.sleep(1)browser.switch_to.window(browser.window_handles[0])browser.get('https://www.zhihu.com') 首先访问我的博客,然后调用了 execute_script() 方法,传入 window.open() 这个 JavaScript 语句开启一个新的选项卡。再调用 window_handles 属性获取当前开启的所有选项卡,返回的是选项卡的代号列表。调用 switch_to_window() 方法来切换选项卡,其中参数是选项卡的代号。输出的选项卡代号列表:1['CDwindow-C9CADF1ED28CE44970655238552A8DCF', 'CDwindow-538D7F81E467746B7BB2D9D82E2D036E']","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫学习","slug":"Python3-学习笔记/爬虫学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫学习/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"Selenium","slug":"Selenium","permalink":"https://www.itrhx.com/tags/Selenium/"}]},{"title":"Python3 爬虫学习笔记 C03","slug":"A31-Python3-spider-C03","date":"2019-08-23T01:28:22.178Z","updated":"2019-09-24T12:43:59.900Z","comments":true,"path":"2019/08/23/A31-Python3-spider-C03/","link":"","permalink":"https://www.itrhx.com/2019/08/23/A31-Python3-spider-C03/","excerpt":"Python3 爬虫学习笔记第三章 ——【Ajax 数据爬取】","text":"Python3 爬虫学习笔记第三章 ——【Ajax 数据爬取】 【3.1】Ajax 简介Ajax — Asynchronous Javascript And XML(异步 JavaScript 和 XML),是指一种创建交互式网页应用的网页开发技术。可以在不重新加载整个网页的情况下,对网页的某部分进行更新。 【3.2】解析真实地址提取以豆瓣电影动作片排行榜为例,地址为:https://movie.douban.com/typerank?type_name=%E5%8A%A8%E4%BD%9C&type=5&interval_id=100:90&action= ,首先使用常用方法来爬取电影信息:12345678import requestsurl = 'https://movie.douban.com/typerank?type_name=%E5%8A%A8%E4%BD%9C&type=5&interval_id=100:90&action='headers = {\"User-Agent\": \"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 SE 2.X MetaSr 1.0\"}response = requests.get(url, headers=headers)print(response.text) 得到的数据里面我们并没有找到电影相关信息:再次分析页面,发现鼠标下滑的时候,页面不刷新,URL 也不变,但是会加载新数据,那么此处就运用了 Ajax,可以使用抓包工具或者浏览器控制台来捕获 Ajax 接口,获取其真实地址,XHR 是 Ajax 特殊的请求类型,返回的是 json 数据,利用浏览器控制台过滤 XHR,随便点击一条请求,可以看到其 Request URL,也就是真实地址,点击 Preview 就可以看到返回的 json 数据。同样,我们可以使用 Fiddler 抓包软件抓取 Ajax 接口:分析其真实地址为:https://movie.douban.com/j/chart/top_list?type=5&interval_id=100%3A90&action=&start=20&limit=20 ,多下滑几次,只有 start 参数发生了改变,观察变化可知:每一次页面将多出20个电影信息,start 为从第几个电影开始,由此就不难进行数据抓取了 代码:1234567891011121314import requestsurl = 'https://movie.douban.com/j/chart/top_list?type=5&interval_id=100%3A90&action=&'page = int(input('请输入想要第几页的数据:'))data = { 'start': (page - 1)*20, 'limit': '20',}headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36',}response = requests.get(url, params=data, headers=headers)print(response.text) 运行代码即可得到电影排行信息:","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫学习","slug":"Python3-学习笔记/爬虫学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫学习/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"Ajax","slug":"Ajax","permalink":"https://www.itrhx.com/tags/Ajax/"}]},{"title":"Python3 爬虫学习笔记 C02","slug":"A30-Python3-spider-C02","date":"2019-08-23T01:28:22.053Z","updated":"2020-03-14T06:19:49.153Z","comments":true,"path":"2019/08/23/A30-Python3-spider-C02/","link":"","permalink":"https://www.itrhx.com/2019/08/23/A30-Python3-spider-C02/","excerpt":"Python3 爬虫学习笔记第二章 ——【基本库 requests 的使用】","text":"Python3 爬虫学习笔记第二章 ——【基本库 requests 的使用】 【2.1】 requests 简介在 Python 中有两种方式可以发送 HTTP 请求,分别是自带的 urllib 库和第三方的 requests 库 requests 模块需要使用 pip install 命令安装安装,相比 urllib,它的 API 更加人性化,使用 requests 可以让 Cookies、登录验证、代理设置等操作更加简便,官网介绍:http://cn.python-requests.org 【2.2】 requests 基本用法示例:123456789import requestsr = requests.get('https://www.itrhx.com/')print(type(r))print(r.encoding)print(r.status_code)print(r.cookies)print(r.json)print(r.text)print(r.content) 输出结果:12345678910<class 'requests.models.Response'>utf-8200<RequestsCookieJar[]><bound method Response.json of <Response [200]>><!DOCTYPE html><html><head> <meta charset=\"utf-8\"> ...... r.encoding:服务器内容使用的文本编码; r.status_code:响应状态码,200 代表成功,4xx 代表客户端错误,5xx 服务器响应错误; r.cookies:返回 Cookies; r.json:Requests 内置 JSON 解码器; r.text:服务器响应内容,根据响应头部的字符编码自动解码; r.content:字节方式的响应体,自动解码 gzip 和 deflate 编码的响应。 【2.3】 requests 构建 GET 请求 【2.3.1】 基本用法示例:123456789import requestsdata = { 'name': 'TRHX', 'age': '20'}r = requests.get(\"http://httpbin.org/get\", params=data)print('编码后的URL:', r.url)print('字符串方式的响应体:', r.text) 输出结果:123456789101112131415编码后的URL: http://httpbin.org/get?name=TRHX&age=20字符串方式的响应体: { \"args\": { \"age\": \"20\", \"name\": \"TRHX\" }, \"headers\": { \"Accept\": \"*/*\", \"Accept-Encoding\": \"gzip, deflate\", \"Host\": \"httpbin.org\", \"User-Agent\": \"python-requests/2.22.0\" }, \"origin\": \"171.115.102.230, 171.115.102.230\", \"url\": \"https://httpbin.org/get?name=TRHX&age=20\"} 【2.3.2】 二进制数据抓取以抓取 GitHub 站点图标为例:12345import requestsr = requests.get(\"https://github.com/favicon.ico\")with open('favicon.ico', 'wb') as f: f.write(r.content) 该代码将会保存站点图标到本地,其他的,比如音频,视频文件都是由二进制码组成的,皆可使用该方法 【2.3.3】 添加 headersheaders 的作用:部分页面禁止 Python 爬虫对其进行爬取,而添加 headers 就可以模拟成浏览器取访问网站,实现数据的爬取,headers 可以在任意网页 F12 检查控制台里面找到,headers 最重要的是 “User-Agent” 字段 以为例知乎,只有加了 headers 才能正常爬取,否则会返回 400 Bad Request 没有任何数据 123456import requestsheaders = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'}r = requests.get(\"https://www.zhihu.com/explore\", headers=headers)print(r.text) 【2.4】 requests 构建 POST 请求示例:12345import requestsdata = {'name': 'TRHX', 'age': '20'}r = requests.post(\"http://httpbin.org/post\", data=data)print(r.text) 输出结果: 1234567891011121314151617181920{ \"args\": {}, \"data\": \"\", \"files\": {}, \"form\": { \"age\": \"22\", \"name\": \"germey\" }, \"headers\": { \"Accept\": \"*/*\", \"Accept-Encoding\": \"gzip, deflate\", \"Content-Length\": \"18\", \"Content-Type\": \"application/x-www-form-urlencoded\", \"Host\": \"httpbin.org\", \"User-Agent\": \"python-requests/2.22.0\" }, \"json\": null, \"origin\": \"171.115.102.230, 171.115.102.230\", \"url\": \"https://httpbin.org/post\"} 有关 POST 和 GET 两种请求的一些区别: POST 更加安全,不会作为 URL 的一部分,不会被缓存,保存在服务器日志、以及浏览器浏览记录中; POST 发送的数据更大,GET 有 URL 长度限制; POST 可以发送更多的数据类型,GET 只能发送 ASCII 字符; POST 比 GET 慢; POST 查询参数在 WebForms 保存,GET 查询参数在 QueryString 保存; POST 用数据的修改和写入,GET 一般用于搜索排序和筛选之类的操作。 【2.5】 requests 高级用法 【2.5.1】 上传文件示例: 12345import requestsfiles = {'file': open('test.png', 'rb')}r = requests.post('http://httpbin.org/post', files=files)print(r.text) 输出结果: 12345678910111213141516171819{ \"args\": {}, \"data\": \"\", \"files\": { \"file\": \"data:application/octet-stream;base64,iVBOR......\" }, \"form\": {}, \"headers\": { \"Accept\": \"*/*\", \"Accept-Encoding\": \"gzip, deflate\", \"Content-Length\": \"81383\", \"Content-Type\": \"multipart/form-data; boundary=e36a8686cd77c79dc02bfe9d1b010f08\", \"Host\": \"httpbin.org\", \"User-Agent\": \"python-requests/2.22.0\" }, \"json\": null, \"origin\": \"171.115.102.230, 171.115.102.230\", \"url\": \"https://httpbin.org/post\"} 【2.5.2】 使用 Cookies对于需要登录后才能获取数据的网页,可以将账号登录的 Cookies 添加到 headers 来实现网页登录爬取,Cookies 可以抓包获取,代码示例: 123456789import requestsheaders = { 'Cookie': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'Host': 'www.zhihu.com', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36',}r = requests.get('https://www.zhihu.com', headers=headers)print(r.text) 【2.5.3】 会话维持 背景介绍:利用 get() 或者 post() 方法来模拟网页请求,相当于是不同的会话,可以理解为用两个浏览器打开了不同的网页; 运用场景:首先使用 post() 方法登录网页,然后再使用 get() 方法请求某个页面信息,如果不利用会话维持,将无法获取页面数据 维持方法:①两次请求设置一样的 cookies,缺点:繁琐;②使用 Session 对象。 Session 对象使用示例: 123456 import requestss = requests.Session()s.get('http://httpbin.org/cookies/set/number/123456789')r = s.get('http://httpbin.org/cookies')print(r.text) 输出结果成功获取到设置的 cookies: 12345{ \"cookies\": { \"number\": \"123456789\" }} 【2.5.4】 SSL 证书验证 SSL 证书是数字证书的一种,由受信任的数字证书颁发机构 CA 在验证服务器身份后颁发,具有服务器身份验证和数据传输加密功能,网站带有 HTTPS 就表明有 SSL 证书 requests 提供了证书验证的功能。当发送 HTTP 请求的时候,它会检查 SSL 证书,verify 参数可以控制是否检查此证书。如果不加 verify 参数,默认为 True,会自动验证。当一个页面的 SSL 证书没有被官方机构认证时,打开页面就会提示“您的连接不是私密连接”,如果没有设置 verify 参数,将会报以下错误: 1requests.exceptions.SSLError: (\"bad handshake: Error([('SSL routines', 'tls_process_server_certificate', 'certificate verify failed')],)\",) 设置 verify 参数代码示例: 1234import requestsresponse = requests.get('https://www.itrhx.com', verify=False)print(response.text) 【2.5.5】 设置代理为什么要设置代理:某些网页有反爬虫机制,频繁请求网页就会出现验证码等,还有可能直接封掉 IP,导致爬取失败;这种情况下就可以设置 proxies 参数。示例: 12345678import requestsproxies = { 'http': 'http://10.10.1.10:1010', 'https': 'http://10.10.1.10:1020',}requests.get('https://www.itrhx.com', proxies=proxies) 免费代理可在西刺代理找到 【2.5.6】 超时设置与 urllib.request.urlopen() 类似,requests 也可以设置 timeout 参数,请求分为两个阶段:连接和读取 设置连接和读取时间总和: 1234import requestsr = requests.get('https://www.itrhx.com', timeout=1)print(r.status_code) 分别设置连接和读取时间: 1234import requestsr = requests.get('https://www.itrhx.com', timeout=(5, 10))print(r.status_code) 永久等待: 123456import requests# 两种方法实现# r = requests.get('https://www.itrhx.com')r = requests.get('https://www.itrhx.com', timeout=None)print(r.status_code)","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫学习","slug":"Python3-学习笔记/爬虫学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫学习/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"requests","slug":"requests","permalink":"https://www.itrhx.com/tags/requests/"}]},{"title":"Python3 爬虫学习笔记 C01","slug":"A29-Python3-spider-C01","date":"2019-08-23T01:28:21.841Z","updated":"2019-09-24T12:39:54.253Z","comments":true,"path":"2019/08/23/A29-Python3-spider-C01/","link":"","permalink":"https://www.itrhx.com/2019/08/23/A29-Python3-spider-C01/","excerpt":"Python3 爬虫学习笔记第一章 ——【基本库 urllib 的使用】","text":"Python3 爬虫学习笔记第一章 ——【基本库 urllib 的使用】 【1.1】 urllib 简介在 Python 中有两种方式可以发送 HTTP 请求,分别是自带的 urllib 库和第三方的 requests 库 urllib 库:Python 内置的 HTTP 请求库,无需额外安装即可使用;Python 2 中有 urllib 和 urllib2 两个库来实现请求的发送,Python 3 中统一为 urllib。官方文档:https://docs.python.org/3/library/urllib.html urllib 所包含的常用模块: urllib.request:模拟发送请求; urllib.error:异常处理模块,用于捕获异常; urllib.parse:解析、拆分、合并URL; urllib.robotparser:读取网站的 robots.txt 文件,判断哪些内容可以爬取。 urllib.request 所包含的常用方法: urllib.request.urlopen():打开网址URL,这可以是一个字符串或一个 Request对象; urllib.request.Request():在请求的时候传入一些 headers 等信息; urllib.request.urlretrieve():将获取的URL的内容写到文件目录中去。 urllib.error 所包含的两个异常: URLError:继承自 OSError 类,是 error 异常模块的基类,由 request 模块产生的异常都可以通过捕获这个类来处理。 HTTPError:是 URLError 的子类,专门用来处理 HTTP 请求错误,比如认证请求失败等。 urllib.parse 所包含的常用方法: urllib.parse.urlencode():将字典参数序列化为 GET 请求参数; urllib.parse.parse_qs():将 GET 请求参数反序列化转回字典; urllib.parse.parse_qsl():将参数转化为元组组成的列表; urllib.parse.urlparse():对 URL 进行分段(返回6个结果); urllib.parse.urlunparse():对 URL 进行组合(长度必须为6); urllib.parse.urlsplit():对 URL 进行分段(不单独解析params部分,返回5个结果); urllib.parse.urlunsplit():对 URL 进行组合(长度必须为5); urllib.parse.urljoin():对 URL 进行组合(没有长度限制,给定两个参数,自动分析 scheme、netloc 和 path 这 3 个内容并对新链接缺失的部分进行补充,最后返回结果); urllib.parse.quote():将内容转化为 URL 编码格式; urllib.parse.unquote():对 URL 进行解码。 urllib.robotparser 所包含的类: RobotFileParser:根据网站的 robots.txt 文件来判断一个爬取爬虫是否有权限来爬取这个网页 【1.2】 urllib.request 发送请求【1.2.1】 urllib.request.urlopen()【1.2.1.1】 基本使用方法urlopen() 函数的 API:1urllib.request.urlopen(url, data=None, [timeout,]*, cafile=None, capath=None, cadefault=False, context=None) 基本使用:运行以下代码可得到 https://www.itrhx.com/ 的网页源代码:1234import urllib.requestresponse = urllib.request.urlopen('https://www.itrhx.com/')print(response.read().decode('utf-8')) 输出响应对象的类型和属性:1234567import urllib.requestresponse = urllib.request.urlopen('https://www.itrhx.com/')print(type(response)) # 响应类型print(response.status) # 返回结果的状态码,200代表请求成功print(response.getheaders()) # 响应的头信息print(response.getheader('Server')) # 获取响应头的 server 值 运行结果:1234<class 'http.client.HTTPResponse'>200[('Content-Type', 'text/html; charset=utf-8'), ('Server', 'GitHub.com'), ('Last-Modified', 'Sat, 17 Aug 2019 12:16:48 GMT'), ('ETag', '\"5d57f030-10863\"'), ('Access-Control-Allow-Origin', '*'), ('Expires', 'Sat, 17 Aug 2019 19:41:25 GMT'), ('Cache-Control', 'max-age=600'), ('X-Proxy-Cache', 'MISS'), ('X-GitHub-Request-Id', 'C748:735D:5B7461:619B95:5D58560B'), ('Content-Length', '67683'), ('Accept-Ranges', 'bytes'), ('Date', 'Sun, 18 Aug 2019 13:28:44 GMT'), ('Via', '1.1 varnish'), ('Age', '228'), ('Connection', 'close'), ('X-Served-By', 'cache-tyo19931-TYO'), ('X-Cache', 'HIT'), ('X-Cache-Hits', '1'), ('X-Timer', 'S1566134924.190474,VS0,VE0'), ('Vary', 'Accept-Encoding'), ('X-Fastly-Request-ID', '25a69f8130fc9cae412d28990a724543d7d05e8b')]GitHub.com 【1.2.1.2】 添加参数根据 urlopen() 函数的 API 可知,除了最基本的 URL 参数以外,我们还可以传递其他内容,比如 data(附加数据)、timeout(超时时间)等,以下用 data 和 timeout 参数举例说明。 ● data 参数如果要添加 data 参数,需要使用 bytes 方法将参数转化为字节流编码格式的内容,即 bytes 类型。另外,如果传递了这个参数,则它的请求方式就不再是 GET 方式,而是 POST 方式。代码示例:123456import urllib.parseimport urllib.requestdata = bytes(urllib.parse.urlencode({'word': 'hello'}), encoding='utf8')response = urllib.request.urlopen('http://httpbin.org/post', data=data)print(response.read()) httpbin.org 站点提供 HTTP 请求测试,http://httpbin.org/post 用于测试 POST 请求,示例中传递一个值为 hello 的 word 参数。使用 bytes 方法,将其转码成 bytes(字节流)类型。该方法的第一个参数需要是 str(字符串)类型,需要用 urllib.parse 模块里的 urlencode 方法来将参数字典转化为字符串;第二个参数指定编码格式为 utf8,运行结果:123456789101112131415161718b'{ \"args\": {}, \"data\": \"\", \"files\": {}, \"form\": { \"word\": \"hello\" }, \"headers\": { \"Accept-Encoding\": \"identity\", \"Content-Length\": \"10\", \"Content-Type\": \"application/x-www-form-urlencoded\", \"Host\": \"httpbin.org\", \"User-Agent\": \"Python-urllib/3.6\" }, \"json\": null, \"origin\": \"171.115.101.10, 171.115.101.10\", \"url\": \"https://httpbin.org/post\"}' ● timeout 参数举例:1234import urllib.requestresponse = urllib.request.urlopen('http://httpbin.org/get', timeout=0.1) print(response.read()) 运行结果:12345678...During handling of the above exception, another exception occurred:Traceback (most recent call last): File \"C:/Users/Lenovo/Desktop/1.py\", line 2, in <module> response = urllib.request.urlopen('http://httpbin.org/get', timeout=0.1) ...urllib.error.URLError: <urlopen error timed out> timeout 设置为0.1,0.1秒过后服务器没有响应,便会抛出 URLError 异常进阶:使用 try except 语句抛出异常 【1.2.2】 urllib.request.Request()Request() 方法可以在请求的时候传入一些 data、headers 等信息Request() 的构造方法:1class urllib.request.Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None) 构造方法各个参数的解释: url:用于请求 URL,这是必传参数,其他都是可选参数。 data:如果要传,必须传 bytes(字节流)类型的。如果它是字典,可以先用 urllib.parse 模块里的 urlencode() 编码。 headers:是一个字典,它就是请求头,可以在构造请求时通过 headers 参数直接构造,也可以通过调用请求实例的 add_header() 方法添加。添加请求头最常用的用法就是通过修改 User-Agent 来伪装浏览器,默认的 User-Agent 是 Python-urllib,我们可以通过修改它来伪装浏览器。 origin_req_host:指的是请求方的 host 名称或者 IP 地址。 unverifiable:表示这个请求是否是无法验证的,默认是 False,意思就是说用户没有足够权限来选择接收这个请求的结果。例如,我们请求一个 HTML 文档中的图片,但是我们没有自动抓取图像的权限,这时 unverifiable 的值就是 True。 method:是一个字符串,用来指示请求使用的方法,比如 GET、POST 和 PUT 等。 简单举例:1234567891011121314import urllib.requestimport urllib.parseurl = 'http://www.baidu.com/'# 定制要伪装的头部headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}# 构建请求对象request = urllib.request.Request(url=url, headers=headers)# 发送请求response = urllib.request.urlopen(request)print(response.read().decode()) 【1.2.3】 urllib.request.urlretrieve()将获取到的 URL 内容保存到当前文件夹,简单举例:123456789import urllib.requesturl = 'https://www.itrhx.com/images/trhx.png'# response = urllib.request.urlopen(image_url)# with open('trhx.png', 'wb') as fp:# fp.write(response.read())urllib.request.urlretrieve(url, 'trhx.png') 【1.3】 urllib.error 异常处理【1.3.1】 URLError如果打开一个不存在的页面,就会出现 URLError 错误,该错误有一个 reason 属性,用于返回错误的原因。简单举例:12345from urllib import request, error try: response = request.urlopen('https://www.itrhx.com/index/') except error.URLError as e: print(e.reason) 输出结果:1Not Found 【1.3.2】 HTTPErrorURLError 的子类,专门用来处理 HTTP 请求错误,比如认证请求失败等。它有如下3个属性: code:返回 HTTP 状态码,比如 404 表示网页不存在,500 表示服务器内部错误等。 reason:同父类一样,用于返回错误的原因。 headers:返回请求头。 简单举例:12345from urllib import request, error try: response = request.urlopen('https://www.itrhx.com/index/') except error.HTTPError as e: print(e.code, e.reason, e.headers) 输出结果:123456789101112131415161718404 Not Found Content-Type: text/html; charset=utf-8Server: GitHub.comETag: \"5d57f030-7f2\"Access-Control-Allow-Origin: *X-Proxy-Cache: MISSX-GitHub-Request-Id: 4B46:2F5D:6DE0F1:755BB2:5D5964C5Content-Length: 2034Accept-Ranges: bytesDate: Sun, 18 Aug 2019 14:50:41 GMTVia: 1.1 varnishAge: 252Connection: closeX-Served-By: cache-tyo19951-TYOX-Cache: HITX-Cache-Hits: 1X-Timer: S1566139842.563134,VS0,VE0Vary: Accept-EncodingX-Fastly-Request-ID: e9eb0a507be66a866bfaa7c5cc2e1c53b1f7ccab 【1.3.3】 进阶用法因为 URLError 是 HTTPError 的父类,所以可以先选择捕获子类的错误,再去捕获父类的错误,前面的代码改进:12345678910from urllib import request, error ​try: response = request.urlopen('https://www.itrhx.com/index/') except error.HTTPError as e: print(e.reason, e.code, e.headers) except error.URLError as e: print(e.reason) else: print('Request Successfully') 【1.4】 urllib.parse 解析 URL【1.4.1】 urllib.parse.urlencode()将字典参数序列化为 GET 请求参数,示例:12345678from urllib.parse import urlencodedata = { 'ie': 'utf-8', 'wd': 'TRHX',}base_url = 'http://www.baidu.com?'url = base_url + urlencode(data)print(url) 输出结果:1http://www.baidu.com?ie=utf-8&wd=TRHX 【1.4.2】 urllib.parse.parse_qs()与 urlencode() 相反,将 GET 请求参数反序列化转回字典,示例:123from urllib.parse import parse_qsquery = 'name=TRHX&age=20'print(parse_qs(query)) 输出结果:1{'name': ['TRHX'], 'age': ['20']} 【1.4.3】 urllib.parse.parse_qsl()将参数转化为元组组成的列表,示例:123from urllib.parse import parse_qslquery = 'name=TRHX&age=20'print(parse_qsl(query)) 输出 结果:1[('name', 'TRHX'), ('age', '20')] 【1.4.4】 urllib.parse.urlparse()对 URL 进行分段,返回 6 个结果,示例:123from urllib.parse import urlparseresult = urlparse('http://www.baidu.com/index.html;user?id=5#comment')print(type(result), result) 输出结果:1<class 'urllib.parse.ParseResult'> ParseResult(scheme='http', netloc='www.baidu.com', path='/index.html', params='user', query='id=5', fragment='comment') 返回结果为 ParseResult 类型的对象,含 scheme、netloc、path、params、query 和 fragment 6 个部分,依次代表协议、域名、路径、参数、查询条件、锚点 【1.4.5】 urllib.parse.urlunparse()与 urlparse() 相反,对 URL 进行组合,传入的参数是一个可迭代对象,长度必须是 6,否则会抛出参数数量不足或者过多的问题,示例:123from urllib.parse import urlunparse data = ['http', 'www.baidu.com', 'index.html', 'user', 'a=6', 'comment'] print(urlunparse(data)) 输出结果:1http://www.baidu.com/index.html;user?a=6#comment 【1.4.6】 urllib.parse.urlsplit()与 urlparse() 方法相似,但是它不再单独解析 params 部分,只返回 5 个结果。params 会合并到 path 中,示例:123from urllib.parse import urlsplit result = urlsplit('http://www.baidu.com/index.html;user?id=5#comment') print(result) 输出结果:1SplitResult(scheme='http', netloc='www.baidu.com', path='/index.html;user', query='id=5', fragment='comment') 【1.4.7】 urllib.parse.urlunsplit()与 urlunparse() 方法类似,对 URL 进行组合,传入的参数也是一个可迭代对象,长度必须为 5,示例:123from urllib.parse import urlunsplit data = ['http', 'www.baidu.com', 'index.html', 'a=6', 'comment'] print(urlunsplit(data)) 输出结果:1http://www.baidu.com/index.html?a=6#comment 【1.4.8】 urllib.parse.urljoin()对 URL 进行组合,提供两个 URL 作为两个参数,将会自动分析 URL 的 scheme、netloc 和 path 这 3 个内容并对新链接缺失的部分进行补充,最后返回结果,示例:123456789from urllib.parse import urljoin print(urljoin('http://www.baidu.com', 'friends.html')) print(urljoin('http://www.baidu.com', 'https://www.itrhx.com/friends.html')) print(urljoin('http://www.baidu.com/friends.html', 'https://www.itrhx.com/friends.html')) print(urljoin('http://www.baidu.com/friends.html', 'https://www.itrhx.com/friends.html?id=2')) print(urljoin('http://www.baidu.com?wd=trhx', 'https://www.itrhx.com/index.html')) print(urljoin('http://www.baidu.com', '?category=2#comment')) print(urljoin('www.baidu.com', '?category=2#comment')) print(urljoin('www.baidu.com#comment', '?category=2')) 输出结果:12345678http://www.baidu.com/friends.htmlhttps://www.itrhx.com/friends.htmlhttps://www.itrhx.com/friends.htmlhttps://www.itrhx.com/friends.html?id=2https://www.itrhx.com/index.htmlhttp://www.baidu.com?category=2#commentwww.baidu.com?category=2#commentwww.baidu.com?category=2 【1.4.9】 urllib.parse.quote()将内容转化为 URL 编码的格式。当 URL 中带有中文参数时,可以将中文字符转化为 URL 编码,示例:1234from urllib.parse import quotekeyword = '中国' url = 'https://www.baidu.com/s?wd=' + quote(keyword) print(url) 输出结果:1https://www.baidu.com/s?wd=%E4%B8%AD%E5%9B%BD 【1.4.10】 urllib.parse.unquote()与 quote() 方法相反,对 URL 进行解码,示例:123from urllib.parse import unquote url = 'https://www.baidu.com/s?wd=%E4%B8%AD%E5%9B%BD' print(unquote(url)) 输出结果:1https://www.baidu.com/s?wd=中国 【1.5】 urllib.robotparser 爬取权限判断【1.5.1】 Robots 协议简介 Robots 协议即爬虫协议,用来告诉爬虫和搜索引擎哪些页面可以抓取,哪些不可以抓取。它通常是一个叫作 robots.txt 的文本文件,一般放在网站的根目录下。 robots.txt 基本格式:123User-agent:Disallow:Allow: User-agent 为搜索爬虫的名称,设置为 * 则表示对任何爬虫皆有效; Disallow 指定了不允许抓取的目录,设置为 / 则代表不允许抓取所有页面; Allow 指定了允许抓取的目录,一般和 Disallow 一起使用,一般不会单独使用,用来排除某些限制。 一些常见的搜索爬虫名称及其对应的网站: 爬虫名称 网站名称 网站地址 BaiduSpider 百度 www.baidu.com Googlebot 谷歌 www.google.com 360Spider 360 www.so.com Sogouspider 搜狗 www.sogou.com YodaoBot 有道 www.youdao.com Bingbot 必应 www.bing.com Yahoo! Slurp 雅虎 www.yahoo.com ia_archiver Alexa www.alexa.cn Scooter altavista www.altavista.com 【1.5.2】 RobotFileParser 类常用方法RobotFileParser 类的声明:1urllib.robotparser.RobotFileParser(url='') 常用方法及其解释: set_url:用来设置 robots.txt 文件的链接。如果在创建 RobotFileParser对象时传入了链接,那么就不需要再用这种方法了。 read:读取 robots.txt 文件并进行分析。此方法执行一个读取和分析操作,若不调用此方法,接下来的判断都会为 False,这个方法不会返回任何内容,但是执行了读取操作。 parse:解析 robots.txt 文件,传入的参数是 robots.txt 某些行的内容,它会按照 robots.txt 的语法规则来分析这些内容。 can_fetch:该方法传入两个参数,第一个是 User-agent,第二个是要抓取的 URL。返回的内容是该搜索引擎是否可以抓取这个 URL,返回结果是 True 或 False。 mtime:返回的是上次抓取和分析 robots.txt 的时间,此方法可以定期检查来抓取最新的 robots.txt。 modified:将当前时间设置为上次抓取和分析 robots.txt 的时间。 以简书为例:123456from urllib.robotparser import RobotFileParserrp = RobotFileParser()rp.set_url('http://www.jianshu.com/robots.txt')rp.read()print(rp.can_fetch('*', 'https://www.jianshu.com/p/6d9527300b4c'))print(rp.can_fetch('*', \"http://www.jianshu.com/search?q=python&page=1&type=collections\")) 输出结果:12FalseFalse","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫学习","slug":"Python3-学习笔记/爬虫学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫学习/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"urllib","slug":"urllib","permalink":"https://www.itrhx.com/tags/urllib/"}]},{"title":"一个 JS 脚本实现网站预加载,提升页面加载速度","slug":"A24-instant.page","date":"2019-08-23T01:27:49.948Z","updated":"2019-09-09T13:44:52.383Z","comments":true,"path":"2019/08/23/A24-instant.page/","link":"","permalink":"https://www.itrhx.com/2019/08/23/A24-instant.page/","excerpt":"instant.page 使用即时预加载技术,在用户点击之前预先加载页面。当用户的鼠标悬停在一个链接上超过 65 毫秒时,浏览器会对此页面进行预加载,当用户点击链接后,就从预加载的缓存中直接读取页面内容,从而达到缩短页面加载时间的目的。","text":"instant.page 使用即时预加载技术,在用户点击之前预先加载页面。当用户的鼠标悬停在一个链接上超过 65 毫秒时,浏览器会对此页面进行预加载,当用户点击链接后,就从预加载的缓存中直接读取页面内容,从而达到缩短页面加载时间的目的。 以我博客为例,使用了这项技术后,当鼠标在一个链接停留超过 65 毫秒时,Network 里可以看见相关文章已经预加载出来了,而停留时间过短就不会预加载(红色部分,状态为 canceled) 使用方法:将以下HTML代码放在</ body> 之前即可:1<script src=\"//instant.page/1.2.2\" type=\"module\" integrity=\"sha384-2xV8M5griQmzyiY3CDqh1dn4z3llDVqZDqzjzcY+jCBCk/a5fXJmuZ/40JJAPeoU\"></script> 但是此脚本是官方的,储存在国外服务器,对国内访问不太友好,可以将该JS脚本储存到自己的服务器上,点此获取该JS脚本,然后再根据以下格式在</ body> 之前引用:1<script src=\"`存放路径`/instantclick-1.2.2.js\" type=\"module\"></script> 也可以直接使用我的,使用 jsDeliver CDN 加速,速度还可以:1<script src=\"https://cdn.jsdelivr.net/gh/TRHX/CDN-for-itrhx.com@2.0.2/js/instantclick-1.2.2.js\" type=\"module\"></script> 参考资料:《网站预加载 JS 脚本 instant.page》——by 左岸 ;instant.page官网","categories":[{"name":"WEB前端","slug":"WEB前端","permalink":"https://www.itrhx.com/categories/WEB前端/"}],"tags":[{"name":"instant.page","slug":"instant-page","permalink":"https://www.itrhx.com/tags/instant-page/"},{"name":"JS 预加载","slug":"JS-预加载","permalink":"https://www.itrhx.com/tags/JS-预加载/"}]},{"title":"网站ICP备案和公安备案流程","slug":"A23-beian","date":"2019-08-23T01:27:49.803Z","updated":"2020-03-14T06:07:46.135Z","comments":true,"path":"2019/08/23/A23-beian/","link":"","permalink":"https://www.itrhx.com/2019/08/23/A23-beian/","excerpt":"","text":"网站备案分为ICP备案和公安备案 ICP备案:ICP备案的目的就是为了防止在网上从事非法的网站经营活动,打击不良互联网信息的传播,如果网站不备案的话,很有可能被查处以后关停。根据中华人民共和国信息产业部第十二次部务会议审议通过的《非经营性互联网信息服务备案管理办法》条例,在中华人民共和国境内提供非经营性互联网信息服务,应当办理备案。未经备案,不得在中华人民共和国境内从事非经营性互联网信息服务。而对于没有备案的网站将予以罚款或关闭。 公安备案:网站备案是根据国家法律法规需要网站的所有者向国家有关部门申请的备案,公安局备案是其中一种。公安局备案一般按照各地公安机关指定的地点和方式进行,操作流程会比ICP备案流程简单,主要是已登记为主。 以百度官网为例,其中京公安网备11000002000001就是公安备案,京ICP证030173号就是ICP备案 – ICP备案 一般在域名服务商那里都会有代备案系统,下面以阿里云为例,进入备案系统: 1、填写信息验证备案类型备案主办单位填写,个人就选个人,企业就选企业,按照实际信息填写: 2、产品验证对搭建备案网站的云服务器进行验证,如果你在阿里云购买了相关产品,就选择相应的产品类型和实例进行验证,也可以勾选已有备案服务号,填写服务号进行验证,备案服务号可以通过备案控制台进行申请,具体操作可以参考官方文档《申请备案服务号》,也有的小伙伴没有在任何地方购买过服务器等相关产品,比如单纯搭建一个 Github Pages + Hexo 轻量级的个人博客,这种博客没有后端,不需要服务器,但是要备案怎么办?这种情况也好解决,去某宝买一个服务号就行了。 3、填写网站信息填写网站信息以及办理备案的个人或者单位的真实信息,在填写网站名称的时候要特别注意!特别注意!特别注意!不满足要求的话是会被打回的!不能使用姓名、地名、成语、不能包含公司、组织等企业性质的词语……具体要求可以参考官方文档《填写主体信息和网站信息》。 4、上传资料根据要求,上传证件照片或证件彩色扫描件。身份证好说,拍好了上传就行了,注意《网站备案信息真实性核验单》需要你下载并打印在一张A4纸上,使用黑色签字笔填写,不能涂改,具体可参照所给的示例进行填写,填写完成后再拍照上传。企业网站类似,提交备案后会在一个工作日内进行初审。 5、人脸核验或幕布拍照核验根据不同地域管局要求及核验平台的支持情况,使用人脸识别进行核验,或者申请专用幕布进行幕布拍照核验 地区 核验要求 上海、福建地区用户 需使用阿里云APP进行人脸核验。如果使用PC端发起的备案申请,请根据界面提示下载阿里云APP进行人脸核验。 广东、辽宁、安徽、重庆地区用户 首次备案、新增网站:支持使用阿里云APP进行人脸核验或通过阿里云备案平台(PC端)进行幕布拍照核验。其他备案类型:需通过阿里云备案平台(PC端)进行幕布拍照核验。 其他地区用户 通过阿里云备案平台(PC端)进行幕布拍照核验。 以幕布拍照核验为例,如果你没有阿里云的幕布,就需要申请幕布(免费的),邮寄很快,大约两三天就到了,等收到幕布后,按照要求进行拍照,一定要仔细阅读拍照说明!一定要仔细阅读拍照说明!一定要仔细阅读拍照说明!不合格依旧会被打回!拍照完成后上传即可。 6、提交管局、短信核验当照片审核通过后,就会提交到管局,工信部要求部分省市成为手机号码短信核验试点省市,相应省市的用户在阿里云备案平台提交备案申请且初审完成后,会收到工信部发送的核验短信,短信包含验证码和验证地址,需要在收到短信的24小时内完成短信核验,备案申请才能进入管局审核。需短信核验省份: 2017年12月18日起:天津、甘肃、西藏、宁夏、海南、新疆、青海被列为试点省份。 2018年9月10日起:浙江、四川、福建、陕西、重庆、广西、云南被列为试点省份。 2018年9月24日起:山东、河南、安徽、湖南、山西、黑龙江、内蒙古、湖北被列为试点省份。 7、ICP备案完成整个备案过程中会有阿里云的客服打电话给你,进行信息确认,备案申请信息成功提交管局系统后,管局审核一般为 3 - 20 个工作日(亲测很快,不到一个周就通过了),审核通过后会收到阿里云的邮件通知。 – 公安备案 公安备案个人觉得比ICP备案还要麻烦,自己在公安备案的时候,最开始申请了一个月也没给我处理(大概是地方原因,所在的市比较小,估计都没几个人办过网站,网警也不太负责),与ICP备案最大的不同,如果你是交互式网站的话,公安备案是需要你去公安机关当面审核的,这也是比较麻烦的一点。 1、用户注册、登录登录全国互联网安全管理服务平台,选择联网备案登录,注册账号并登录 2、新办网站备案申请点击新办网站申请,按实填写网站开办主体,上传身份证正反照和手持身份证件照。 3、填写网站基本信息按实填写网站基本信息,需要注意的地方: IP:IP地址为阿里云/腾讯云的公网IP地址,请不要填写内网IP。 域名证书:以阿里云为例,进入【域名控制台】,点击域名后面的【管理】,选择【域名证书下载】即可,其它服务商类似。 网络接入/域名注册服务商:若办理公安备案的域名是通过阿里云完成的工信部备案,则按照以下填写:网络接入服务商: 接入商所属地区管辖:境内 接入商所属区域 :浙江省 杭州市 滨江区 名称:阿里云计算有限公司 网站接入方式:租赁虚拟空间 域名注册服务商: 域名商所属地区管辖:境内 域名服务商所属区域:浙江省 杭州市 余杭区 名称:阿里云计算有限公司(原万网) 也可以通过点击后面的查询网络接入\\域名注册服务商直接选择相应服务商,其他服务商类似 服务类型:交互式服务指:为互联网用户提供信息发布、交流互动等服务,包括但不限于论坛、博客、微博、网络购物、网上支付等服务类型,此项选择是否提供互联网交互服务将会直接影响到后面是否需要去公安局当面核验,若选择是,当地网警会打电话叫你去公安局当面核验,还需要填写《交互式服务安全检查表》等各种文件,总之是比较麻烦的,个人小网站,博客什么的建议选择否,选择www服务,这样的话不用去当面核验,审核下来也比较快,企业单位用户建议选择交互式。 其他信息如实填写即可! 4、填写网站负责人信息填写网站安全负责人和网站应急联络人相关信息,网站应急联络人直接勾选同主体负责人后会自动填入。 5、同意责任书并提交审核《互联网信息服务单位网络安全责任告知书》有30秒的强制阅读时间,建议认真阅读一下告知书的内容。然后勾选我已阅读,点击提交即可。随后可以看到审核状态,不同地区政策有所不同,会有当地的网警联系网站负责人的,审核通过后记得在网站首页底部张贴公安机关核发的备案图标!","categories":[{"name":"Hexo","slug":"Hexo","permalink":"https://www.itrhx.com/categories/Hexo/"},{"name":"WEB前端","slug":"WEB前端","permalink":"https://www.itrhx.com/categories/WEB前端/"}],"tags":[{"name":"ICP备案","slug":"ICP备案","permalink":"https://www.itrhx.com/tags/ICP备案/"},{"name":"公安备案","slug":"公安备案","permalink":"https://www.itrhx.com/tags/公安备案/"}]},{"title":"恶意刷留言者——你是什么垃圾?","slug":"A25-SB","date":"2019-08-23T01:27:46.962Z","updated":"2020-03-14T06:11:57.038Z","comments":true,"path":"2019/08/23/A25-SB/","link":"","permalink":"https://www.itrhx.com/2019/08/23/A25-SB/","excerpt":"","text":"有一种动物,自认为自己技术了得,实则和CXK差不多,以攻击他人为乐,这种动物称为程序员中的垃圾,哦!不,这种动物称不上程序员! 这个周连续被人刷垃圾评论,具体开始时间不记得了,不想多说什么,太多的文字用在垃圾身上简直是玷污中华上下五千年的文化,只问一句,你是什么垃圾? 随便提一句,这家伙连我的情侣博客一起刷的,真是让人大跌眼镜啊,估计自己没女朋友吧,见不得别人好,悲催啊,不知道又是哪个学校,哪个公司,哪个家庭摊上了这种垃圾。 请问你是什么垃圾?垃圾分类,从我做起!","categories":[{"name":"BLOG","slug":"BLOG","permalink":"https://www.itrhx.com/categories/BLOG/"}],"tags":[{"name":"垃圾","slug":"垃圾","permalink":"https://www.itrhx.com/tags/垃圾/"}]},{"title":"利用官方支持为基于GitHub Pages的Hexo博客启用HTTPS","slug":"A28-hexo-add-https","date":"2019-08-11T14:16:09.175Z","updated":"2019-12-31T15:24:54.792Z","comments":true,"path":"2019/08/11/A28-hexo-add-https/","link":"","permalink":"https://www.itrhx.com/2019/08/11/A28-hexo-add-https/","excerpt":"利用官方支持为基于GitHub Pages的Hexo博客启用HTTPS","text":"利用官方支持为基于GitHub Pages的Hexo博客启用HTTPS HTTP(超文本传输协议),是一个基于请求与响应,无状态的,应用层的协议,常基于TCP/IP协议传输数据,互联网上应用最为广泛的一种网络协议,所有的WWW文件都必须遵守这个标准。设计HTTP的初衷是为了提供一种发布和接收HTML页面的方法。 HTTPS(超文本传输安全协议),是以安全为目标的HTTP通道,简单讲是HTTP的安全版。即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。它是一个URI scheme(抽象标识符体系),句法类同http:体系。用于安全的HTTP数据传输。 目前大多数基于 GitHub Pages 的 Hexo 博客都是利用 CloudFlare 的 CDN 中转来启用 HTTPS 的,实现方法可以参考我的文章:《利用Cloudflare为基于GitHub Pages的Hexo博客添加HTTPS支持》,这样的做法确实可以起到开启HTTPS的目的,但是这样做也有弊端,你会发现 CDN 中转,国外访问的话,可以起到加速的作用,但是国内访问反而速度降低了,还不如直接连接GitHub呢 其实 GitHub 官方是支持自定义域名开启 HTTPS 的,之前我和大多数人一样,以为只有 GitHub Pages 自带的域名(xxx.github.io)才能开启 HTTPS,直到有一天我发现了官方在2018年5月1日发表的博客:《Custom domains on GitHub Pages gain support for HTTPS》,大概讲的意思就是从8月份开始, GitHub Pages 上的自定义域名也能开启 HTTPS 了,下面就具体介绍一下如何实现 如果你以前域名的记录类型是 CNAME 方式,那么就不需要做任何更改如果你以前域名的记录类型是 A 方式,那么就需要把记录值指向以下IP地址: 185.199.108.153 185.199.109.153 185.199.110.153 185.199.111.153 修改好记录值后,我们需要再次来到你博客的 GitHub 仓库,在仓库的【Settings】- 【GitHub Pages】下勾选【Enforce HTTPS】,注意,如果此时你不能勾选,请删除【Custom domain】里面你的域名并点击【Save】保存,刷新网页后就可以勾选了,然后在把域名填进去并保存即可,短时间可能会出现不安全的提示,这是因为加密证书大概一个小时左右才会生效,等一会儿就好了 最后贴一个我的域名解析,可作为参考:","categories":[{"name":"Hexo","slug":"Hexo","permalink":"https://www.itrhx.com/categories/Hexo/"}],"tags":[{"name":"Hexo","slug":"Hexo","permalink":"https://www.itrhx.com/tags/Hexo/"},{"name":"HTTPS","slug":"HTTPS","permalink":"https://www.itrhx.com/tags/HTTPS/"}]},{"title":"Github+jsDelivr+PicGo 打造稳定快速、高效免费图床","slug":"A27-image-hosting","date":"2019-07-31T16:02:08.497Z","updated":"2020-06-08T03:31:04.290Z","comments":true,"path":"2019/08/01/A27-image-hosting/","link":"","permalink":"https://www.itrhx.com/2019/08/01/A27-image-hosting/","excerpt":"","text":"– 前言图床是个啥东西就不用过多介绍了,先来对比一下各路图床: 微博图床:以前用的人比较多,从2019年4月开始开启了防盗链,凉凉 SM.MS:运营四年多了,也变得越来越慢了,到了晚上直接打不开图片,速度堪忧 其他小众图床:随时有挂掉的风险 Imgur等国外图床:国内访问速度太慢,随时有被墙的风险 大厂储存服务:例如七牛云、又拍云、腾讯云COS、阿里云OSS等,容量限制,操作繁琐,又是实名认证又是域名备案的,麻烦,而且还要花钱(有钱又不怕麻烦的当我没说) 因此,GitHub 图床是个不错的选择,利用 jsDelivr CDN 加速访问(jsDelivr 是一个免费开源的 CDN 解决方案),PicGo 工具一键上传,操作简单高效,GitHub 和 jsDelivr 都是大厂,不用担心跑路问题,不用担心速度和容量问题,而且完全免费,可以说是目前免费图床的最佳解决方案! – 新建GitHub仓库登录/注册GitHub,新建一个仓库,填写好仓库名,仓库描述,根据需求选择是否为仓库初始化一个README.md描述文件 – 生成一个Token在主页依次选择【Settings】-【Developer settings】-【Personal access tokens】-【Generate new token】,填写好描述,勾选【repo】,然后点击【Generate token】生成一个Token,注意这个Token只会显示一次,自己先保存下来,或者等后面配置好PicGo后再关闭此网页 – 配置PicGo前往下载PicGo,安装好后开始配置图床 设定仓库名:按照【用户名/图床仓库名】的格式填写 设定分支名:【master】 设定Token:粘贴之前生成的【Token】 指定存储路径:填写想要储存的路径,如【ITRHX-PIC/】,这样就会在仓库下创建一个名为 ITRHX-PIC 的文件夹,图片将会储存在此文件夹中 设定自定义域名:它的作用是,在图片上传后,PicGo 会按照【自定义域名+储存路径+上传的图片名】的方式生成访问链接,并放到粘贴板上,因为我们要使用 jsDelivr 加速访问,所以可以设置为【https://cdn.jsdelivr.net/gh/用户名/图床仓库名 】,上传完毕后,我们就可以通过【https://cdn.jsdelivr.net/gh/用户名/图床仓库名/图片路径 】加速访问我们的图片了,比如上图的图片链接为:https://cdn.jsdelivr.net/gh/TRHX/ImageHosting/ITRHX-PIC/A27/08.jpg 关于 jsDelivr 具体是如何引用资源的可以参考我的另一篇博客:《免费CDN:jsDelivr+Github》 – 进行高效创作配置好PicGo后,我们就可以进行高效创作了,将图片拖拽到上传区,将会自动上传并复制访问链接,将链接粘贴到博文中就行了,访问速度杠杠的,此外PicGo还有相册功能,可以对已上传的图片进行删除,修改链接等快捷操作,PicGo还可以生成不同格式的链接、支持批量上传、快捷键上传、自定义链接格式、上传前重命名等,更多功能自己去探索吧!","categories":[{"name":"图床","slug":"图床","permalink":"https://www.itrhx.com/categories/图床/"}],"tags":[{"name":"jsDelivr","slug":"jsDelivr","permalink":"https://www.itrhx.com/tags/jsDelivr/"},{"name":"图床","slug":"图床","permalink":"https://www.itrhx.com/tags/图床/"},{"name":"PicGo","slug":"PicGo","permalink":"https://www.itrhx.com/tags/PicGo/"}]},{"title":"利用Cloudflare为基于GitHub Pages的Hexo博客添加HTTPS支持","slug":"A26-hexo-add-https","date":"2019-07-31T03:48:43.215Z","updated":"2020-03-14T06:13:57.930Z","comments":true,"path":"2019/07/31/A26-hexo-add-https/","link":"","permalink":"https://www.itrhx.com/2019/07/31/A26-hexo-add-https/","excerpt":"利用Cloudflare为基于GitHub Pages的Hexo博客添加HTTPS支持","text":"利用Cloudflare为基于GitHub Pages的Hexo博客添加HTTPS支持 HTTP(超文本传输协议),是一个基于请求与响应,无状态的,应用层的协议,常基于TCP/IP协议传输数据,互联网上应用最为广泛的一种网络协议,所有的WWW文件都必须遵守这个标准。设计HTTP的初衷是为了提供一种发布和接收HTML页面的方法。 HTTPS(超文本传输安全协议),是以安全为目标的HTTP通道,简单讲是HTTP的安全版。即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。它是一个URI scheme(抽象标识符体系),句法类同http:体系。用于安全的HTTP数据传输。 – 前言GitHub Pages 自带的域名(xxx.github.io)支持开启 https 服务,可以在仓库的【Settings】- 【GitHub Pages】下勾选【Enforce HTTPS】即可,但是如果你设置了自定义域名的话,就比较复杂了,因为 hexo 博客是托管在 GitHub 上的,没有自己的服务器,因此也不支持上传 SSL 证书,从2018年5月1日起,GitHub官方也支持自定义域名开启https了,实现方法可参考我的文章:《利用官方支持为基于GitHub Pages的Hexo博客启用HTTPS》,另外一种方法就是利用 Cloudflare 的 CDN 中转来启用 HTTPS,这种方法的弊端就是国内访问速度可能会变慢,本文主要讲述这种方法 Cloudflare 是一家美国的跨国科技企业,以向客户提供网站安全管理、性能优化及相关的技术支持为主要业务,它提供了免费的 https 服务,注意不是应用SSL证书,实现原理:用户到CDN服务器的连接为 https 方式,而CDN服务器到 GithubPages 服务器的连接为 http 方式,在CDN服务器那里加上反向代理 – 注册 Cloudflare到 Cloudflare官网 注册账号 – 添加站点添加你的站点,一直下一步即可 如果你已经在域名服务商那里解析过域名的话,之后就会出现你域名的解析列表,如果还没有解析过,可以参考《为hexo博客配置个性域名》 –修改DNS点击下一步 Cloudflare 会提供给你两个 DNS 地址 到域名服务商那里修改DNS,以阿里云为例,依次选择【控制台】-【域名】,选择你的域名,点击【管理】-【修改DNS】,将上面 Cloudflare 提供的两个 DNS 地址填进去,会过几分钟才生效 –开启 HTTPS在 Cloudflare 管理页面,点击【Crypto】选项,选择 SSL 的模式为【full】,注意:在CloudFlare 上激活站点后,可能需要24小时才能颁发新证书,耐心等待即可 关于三种模式 Flexible、Full、Full (Strict) 的区别: Flexible:访客与 Cloudflare 之间是加密的,Cloudflare 到站点服务器是不加密的 Full:访客到 Cloudflare、Cloudflare 到站点服务器都是加密的,它不会验证你服务器上的证书是否合法,因此你可以在你服务器上安装任何证书,包括自签名证书 Full (strict):访客到 Cloudflare、Cloudflare 到站点服务器都是加密的,它会验证你服务器上的证书是否合法,你必须在你的服务器上安装有可信赖的CA证书,并且这个证书必须是未过期,包含有域名等信息的 至此,我们的域名就支持 https 访问了,但是当用户输入 http://xxxxxx 访问时,浏览器依旧会以 http 协议来访问,并不会跳转到 https,这时候就需要利用重定向来解决了 –重定向强制 HTTPSCloudflare 提供了一个名叫 Page Rules 的页面规则的功能,我们可以利用此功能对 URL 做一些处理,当用户访问是 HTTP 的时候重定向到 HTTPS,点击【Page Rules】选项,点击【Create Page Rules】,新建如下规则并保存即可 现在我们的 Hexo 博客就实现了全站 HTTPS!","categories":[{"name":"Hexo","slug":"Hexo","permalink":"https://www.itrhx.com/categories/Hexo/"}],"tags":[{"name":"Hexo","slug":"Hexo","permalink":"https://www.itrhx.com/tags/Hexo/"},{"name":"HTTPS","slug":"HTTPS","permalink":"https://www.itrhx.com/tags/HTTPS/"}]},{"title":"Eclipse 通过 JDBC 连接 SQL Server","slug":"A22-eclipse-connects-to-sql","date":"2019-05-13T18:21:23.785Z","updated":"2020-03-14T05:59:46.063Z","comments":true,"path":"2019/05/14/A22-eclipse-connects-to-sql/","link":"","permalink":"https://www.itrhx.com/2019/05/14/A22-eclipse-connects-to-sql/","excerpt":"","text":"本文用到的软件版本以及相关环境: Eclipse Photon Release (4.8.0)JDK-10.0.2SQL Server 2012 1.配置 SQL Server 2012打开 SQL Server Management Studio,使用 SQL Server 身份验证 登录: 如果在安装 SQL Server 2012 时选用了Windows身份验证登录方式,则需要重新设置,设置方法参考:《SQL Server 登录更换【Windows身份验证】为【SQL Server 身份验证】》 登录成功后,打开 SQL Server 配置管理器: 在左边找到 SQL Server 网络配置,点击【你的数据库名】的协议,将右边栏的 Shared Memory、Named Pipes、TCP/IP 全部右键选择启用: 双击 TCP/IP(或者右键选择属性),选择【IP地址】,将【IP1】和【IP10】的【IP地址】设为 127.0.0.1 将所有【IPx】(IP1、IP10、IP11、IP12等)的【已启用】设为是 下拉到窗口底部,将 【IPAll】 中的【TCP端口】设成 1433,其余不变 2.开启 Telnet 服务打开【控制面板】,选择【程序】,点击【启用或关闭 Windows 功能】,找到【Telnet Client】勾选并保存,Windows 7 或者以下的版本则勾选【Telnet 服务器】和【Telnet 客户端】 3.测试1433端口是否打开 运行cmd,输入 telnet 127.0.0.1 1433,若提示连接失败,则说明1433端口没有打开,需要重新进行以上配置,若连接成功,则显示如下: 4.下载JDBC 点击此处下载各个版本JDBC,不同版本的JDBC驱动程序适用的JAR不同,与不同版本的SQL兼容性也不同,具体参考《Microsoft SQL Server JDBC 驱动程序支持矩阵》,比如使用 SQL Server 2012 我们可以下载6.0的版本,下载sqljdbc_6.0.8112.200_chs.tar.gz文件,解压后可以找到sqljdbc41.jar与sqljdbc42.jar文件,使用时要注意自己JDK是哪个版本的,1.80以上的则对应 sqljdbc42.jar 类库 5.Eclipse 连接 SQL Server将 sqljdbc41.jar 或者 sqljdbc42.jar 放到一个文件夹下,打开 Eclipse,在需要连接数据库的项目里,右键【src】,选择【Build Path】、【Configure Build Path…】,在弹出的窗口选择【Libraries】,选择【Modulepath】,单击【Add External JARs…】,找到下载的 sqljdbc41.jar 或者 sqljdbc42.jar 文件并打开,然后【Apply and Close】保存 6.测试连接打开 SQL Server 2012,在其中新建数据库 test Eclipse中,在项目下新建一个package,再新建一个class,用于测试数据库的连接:12345678910111213141516171819202122package test;import java.sql.*;public class Main { public static void main(String [] args) { String driverName=\"com.microsoft.sqlserver.jdbc.SQLServerDriver\"; String dbURL=\"jdbc:sqlserver://localhost:1433;DatabaseName=test\"; //要连接的数据库名 String userName=\"sa\"; //数据库用户名 String userPwd=\"000000\"; //数据库密码 try { Class.forName(driverName); Connection dbConn=DriverManager.getConnection(dbURL,userName,userPwd); System.out.println(\"连接数据库成功\"); } catch(Exception e) { e.printStackTrace(); System.out.print(\"连接失败\"); } } } 如果以上所有操作正确,就能成功连接数据库了:","categories":[{"name":"Java","slug":"Java","permalink":"https://www.itrhx.com/categories/Java/"}],"tags":[{"name":"JDBC","slug":"JDBC","permalink":"https://www.itrhx.com/tags/JDBC/"},{"name":"SQL Server 2012","slug":"SQL-Server-2012","permalink":"https://www.itrhx.com/tags/SQL-Server-2012/"},{"name":"Elicpse","slug":"Elicpse","permalink":"https://www.itrhx.com/tags/Elicpse/"}]},{"title":"Python PEP8 代码规范常见问题及解决方法","slug":"A21-PEP8","date":"2019-04-14T17:09:58.738Z","updated":"2019-09-24T12:47:14.895Z","comments":true,"path":"2019/04/15/A21-PEP8/","link":"","permalink":"https://www.itrhx.com/2019/04/15/A21-PEP8/","excerpt":"之前一直用 Python IDLE 写代码,最近换成 PyCharm 写代码总是会出现波浪号,这才了解到 Python 的 PEP8 代码规范,所以将常见的 PEP8 代码规范问题和解决方法记录一下,学习一下,遇到了再持续更新,养成良好的习惯,编写规范的代码!","text":"之前一直用 Python IDLE 写代码,最近换成 PyCharm 写代码总是会出现波浪号,这才了解到 Python 的 PEP8 代码规范,所以将常见的 PEP8 代码规范问题和解决方法记录一下,学习一下,遇到了再持续更新,养成良好的习惯,编写规范的代码! PEP 8: no newline at end of file解决方法:代码末尾需要另起一行,光标移到最后回车即可 PEP 8: indentation is not a multiple of four解决方法:缩进不是4的倍数,检查缩进 PEP 8: over-indented解决方法:过度缩进,检查缩进 PEP 8: missing whitespace after’,’解决方法:逗号后面少了空格,添加空格即可,类似还有分号或者冒号后面少了空格 PEP 8: multiple imports on one line解决方法:不要在一句 import 中引用多个库,举例:import socket, urllib.error最好写成:import socket import urllib.error PEP 8: blank line at end of line解决方法:代码末尾行多了空格,删除空格即可 PEP 8: at least two spaces before inline comment解决方法:代码与注释之间至少要有两个空格 PEP 8: block comment should start with ‘#’解决方法:注释要以#加一个空格开始 PEP 8: inline comment should start with ‘#’解决方法:注释要以#加一个空格开始 PEP 8: module level import not at top of file解决方法:import不在文件的最上面,可能之前还有其它代码 PEP 8: expected 2 blank lines,found 0解决方法:需要两条空白行,添加两个空白行即可 PEP 8: function name should be lowercase解决方法:函数名改成小写即可 PEP 8: missing whitespace around operator解决方法:操作符(’=’、’>’、’<’等)前后缺少空格,加上即可 PEP 8: unexpected spaces around keyword / parameter equals解决方法:关键字/参数等号周围出现意外空格,去掉空格即可 PEP 8: multiple statements on one line (colon)解决方法:多行语句写到一行了,比如:if x == 2: print('OK')要分成两行写 PEP 8: line too long (82 > 79 characters)解决方法:超过了每行的最大长度限制79 如果想要选择性忽略PEP8代码风格的警告信息可以使用以下方法:(养成良好的习惯,编写规范的代码!不推荐忽略!) ①将鼠标移到出现警告信息的地方,按 alt+Enter,选择忽略(Ignore)这个错误即可:②依次选择 File - Settings - Editor - Inspections,在 Python下找到 PEP8 coding style violation 选项,在右下角的 Ignore errors 里点击加号可以添加需要忽略的警告信息ID(ID信息见后面附录),例如想要忽略indentation contains mixed spaces and tabs这个警告,只需要添加其ID:E101 即可附录:全部警告信息以及对应的ID,官方地址:https://pep8.readthedocs.io/en/latest/intro.html#error-codes code sample message E1 Indentation E101 indentation contains mixed spaces and tabs E111 indentation is not a multiple of four E112 expected an indented block E113 unexpected indentation E114 indentation is not a multiple of four (comment) E115 expected an indented block (comment) E116 unexpected indentation (comment) E117 over-indented E121 (*^) continuation line under-indented for hanging indent E122 (^) continuation line missing indentation or outdented E123 (*) closing bracket does not match indentation of opening bracket’s line E124 (^) closing bracket does not match visual indentation E125 (^) continuation line with same indent as next logical line E126 (*^) continuation line over-indented for hanging indent E127 (^) continuation line over-indented for visual indent E128 (^) continuation line under-indented for visual indent E129 (^) visually indented line with same indent as next logical line E131 (^) continuation line unaligned for hanging indent E133 (*) closing bracket is missing indentation E2 Whitespace E201 whitespace after ‘(‘ E202 whitespace before ‘)’ E203 whitespace before ‘:’ E211 whitespace before ‘(‘ E221 multiple spaces before operator E222 multiple spaces after operator E223 tab before operator E224 tab after operator E225 missing whitespace around operator E226 (*) missing whitespace around arithmetic operator E227 missing whitespace around bitwise or shift operator E228 missing whitespace around modulo operator E231 missing whitespace after ‘,’, ‘;’, or ‘:’ E241 (*) multiple spaces after ‘,’ E242 (*) tab after ‘,’ E251 unexpected spaces around keyword / parameter equals E261 at least two spaces before inline comment E262 inline comment should start with ‘# ‘ E265 block comment should start with ‘# ‘ E266 too many leading ‘#’ for block comment E271 multiple spaces after keyword E272 multiple spaces before keyword E273 tab after keyword E274 tab before keyword E275 missing whitespace after keyword E3 Blank line E301 expected 1 blank line, found 0 E302 expected 2 blank lines, found 0 E303 too many blank lines (3) E304 blank lines found after function decorator E305 expected 2 blank lines after end of function or class E306 expected 1 blank line before a nested definition E4 Import E401 multiple imports on one line E402 module level import not at top of file E5 Line length E501 (^) line too long (82 > 79 characters) E502 the backslash is redundant between brackets E7 Statement E701 multiple statements on one line (colon) E702 multiple statements on one line (semicolon) E703 statement ends with a semicolon E704 (*) multiple statements on one line (def) E711 (^) comparison to None should be ‘if cond is None:’ E712 (^) comparison to True should be ‘if cond is True:’ or ‘if cond:’ E713 test for membership should be ‘not in’ E714 test for object identity should be ‘is not’ E721 (^) do not compare types, use ‘isinstance()’ E722 do not use bare except, specify exception instead E731 do not assign a lambda expression, use a def E741 do not use variables named ‘l’, ‘O’, or ‘I’ E742 do not define classes named ‘l’, ‘O’, or ‘I’ E743 do not define functions named ‘l’, ‘O’, or ‘I’ E9 Runtime E901 SyntaxError or IndentationError E902 IOError W1 Indentation warning W191 indentation contains tabs W2 Whitespace warning W291 trailing whitespace W292 no newline at end of file W293 blank line contains whitespace W3 Blank line warning W391 blank line at end of file W5 Line break warning W503 (*) line break before binary operator W504 (*) line break after binary operator W505 (*^) doc line too long (82 > 79 characters) W6 Deprecation warning W601 .has_key() is deprecated, use ‘in’ W602 deprecated form of raising exception W603 ‘<>’ is deprecated, use ‘!=’ W604 backticks are deprecated, use ‘repr()’ W605 invalid escape sequence ‘x’ W606 ‘async’ and ‘await’ are reserved keywords starting with Python 3.7","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"学习经验","slug":"Python3-学习笔记/学习经验","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/学习经验/"}],"tags":[{"name":"Python","slug":"Python","permalink":"https://www.itrhx.com/tags/Python/"},{"name":"PEP8","slug":"PEP8","permalink":"https://www.itrhx.com/tags/PEP8/"}]},{"title":"VMware Pro 15 安装 Deepin15.9 国产操作系统","slug":"A20-install-deepin15.9","date":"2019-04-14T12:53:34.310Z","updated":"2020-03-14T05:53:55.551Z","comments":true,"path":"2019/04/14/A20-install-deepin15.9/","link":"","permalink":"https://www.itrhx.com/2019/04/14/A20-install-deepin15.9/","excerpt":"","text":"Deepin是由武汉深之度科技有限公司开发的Linux发行版,个人认为其界面设计非常美观,而且作为国产操作系统,值得我们去体验和支持! 1.下载安装 VMware Workstation Pro 15 进入 VMware 官网或者在软件商店下载最新版VMware虚拟机并安装 2.下载 Deepin15.9 系统 进入 deepin 官网,下载最新版 deepin 系统镜像 3.在 VMware 中创建虚拟机打开安装好的 VMware Workstation Pro 15,选择创建新的虚拟机在新建虚拟机向导中选择自定义(高级):默认直接下一步,直到出现下图,再选择稍后安装操作系统:选择客户机操作系统为 Linux ,如果你电脑是32位就选择 Ubuntu 版本,64位就选择 Ubuntu 64 位版本:更改虚拟机名称及存放位置:接下来为虚拟机指定处理器数量、分配内存(太大了可能会导致卡顿,太小了也不好,推荐内存大小即可)一直选择默认即可,选择磁盘时,选择创建新虚拟磁盘:选择将虚拟磁盘储存为单个文件:默认下一步:点击完成:此时我们就可以在虚拟机左侧“我的计算机”下面看到刚刚创建的虚拟机 Deepin,单击 Deepin,选择“编辑虚拟机设置”, 再选择“CD/DVD(SATA)”,选择“使用ISO映像文件”,点击“浏览”,找到先前我们下载好的 Deepin 15.9 镜像文件,点击“确定” 4.在虚拟机上安装 Deepin 系统单击 Deepin,选择“开启此虚拟机”接下来就是选择语言、创建用户、选择时区、指定磁盘等过程:安装完成后:可以看见界面还是相当美观的,系统也自带了深度的一些软件,比如深度录屏,深度录音,深度影院,深度计算器等等的一些小工具,作为国产操作系统,个人觉得已经非常优秀了,值得去体验!","categories":[{"name":"Linux","slug":"Linux","permalink":"https://www.itrhx.com/categories/Linux/"}],"tags":[{"name":"VMware","slug":"VMware","permalink":"https://www.itrhx.com/tags/VMware/"},{"name":"Deepin","slug":"Deepin","permalink":"https://www.itrhx.com/tags/Deepin/"}]},{"title":"Windows 系统中 Pygame 的安装","slug":"A19-install-pygame","date":"2019-03-10T14:34:09.591Z","updated":"2019-09-24T12:47:07.382Z","comments":true,"path":"2019/03/10/A19-install-pygame/","link":"","permalink":"https://www.itrhx.com/2019/03/10/A19-install-pygame/","excerpt":"","text":"Pygame是跨平台Python模块,专为电子游戏设计,可用于管理图形、动画乃至声音,建立在SDL基础上,允许实时电子游戏研发而无需被低级语言(如机器语言和汇编语言)束缚,通过使用Pygame来处理在屏幕上绘制图像等任务,你不用考虑众多繁琐而艰难的编码工作,而是将重点放在程序的高级逻辑上。 你可以从以下三个地址查找与你运行的Python版本相匹配的Windows安装程序: https://bitbucket.org/pygame/pygame/downloads/ (Pygame项目托管在代码分享网站Bitbucket中) http://www.pygame.org/download.shtml (Pygame官网) https://www.lfd.uci.edu/~gohlke/pythonlibs/#pygame (如果以上两个地址找不到合适的安装程序,推荐去这个) 如果下载的是.exe文件,直接运行它,如果下载的是.whl文件,就需要打开命令窗口,切换到该文件所在的目录,使用pip来运行它: 首先检查电脑是否安装了pip,打开终端窗口,执行如下命令:1>python -m pip --version 如果输出版本信息则已安装:1>pip 18.1 from E:\\Python\\lib\\site-packages\\pip (python 3.6) 否则请安装pip,访问 https://bootstrap.pypa.io/get-pip.py ,如果出现对话框请直接保存文件,如果出现的是get-pip.py的源代码,则需要新建一个get-pip.py文件,将该代码复制粘贴到其中,使用下面的命令运行get-pip.py:1>python get-pip.py 安装完成后可再次使用python -m pip --version命令检查是否成功安装了pip,成功安装pip后,使用以下命令来安装Pygame:(注意要先cd到你下载的文件的目录)1>python -m pip install --user 下载的.whl文件名 出现以下信息则表示安装成功:1>Successfully installed 你安装的Pygame版本 比如我的Python版本是3.6.5,64位的,则需要下载pygame‑1.9.4‑cp36‑cp36m‑win_amd64.whl,该文件保存到了桌面,使用下面的命令安装Pygame:12345678C:\\Users\\Lenovo>cd desktopC:\\Users\\Lenovo\\Desktop>python -m pip install --user pygame‑1.9.4‑cp36‑cp36m‑win_amd64.whlProcessing c:\\users\\lenovo\\desktop\\pygame‑1.9.4‑cp36‑cp36m‑win_amd64.whlInstalling collected packages: pygameSuccessfully installed pygame‑1.9.4C:\\Users\\Lenovo>Desktop> 检查是否成功安装Pygame:在Python的IDLE里输入import pygame,如果不报错,则安装成功,再输入pygame.ver就能看到版本号: ) 可能出现的问题:报错:xxxxxxxxxxxxxxxxxxxxxx.whl is not a supported wheel on this platform.原因:Python版本与Pygame版本不对应解决方法:Pygame文件名中的cp**表示Python对应的版本,另外并不是你电脑64位则下载64位,要看你安装的Python是否为64位,注意下载对应的版本! 报错:You are using pip version x.x.x, however version x.x.x is available.You should consider upgrading via the 'python -m pip install --upgrade pip' command.原因:版本需要更新解决方法:输入python -m pip install --upgrade pip命令进行更新即可 Pygame安装完成后我们就可以使用Python来开发游戏啦!","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"学习经验","slug":"Python3-学习笔记/学习经验","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/学习经验/"}],"tags":[{"name":"Pygame","slug":"Pygame","permalink":"https://www.itrhx.com/tags/Pygame/"},{"name":"Python","slug":"Python","permalink":"https://www.itrhx.com/tags/Python/"}]},{"title":"免费CDN:jsDelivr + Github","slug":"A18-free-cdn","date":"2019-02-10T14:30:17.903Z","updated":"2020-03-14T05:51:31.881Z","comments":true,"path":"2019/02/10/A18-free-cdn/","link":"","permalink":"https://www.itrhx.com/2019/02/10/A18-free-cdn/","excerpt":"","text":"本文有参考《jsDelivr+github使用教程,免费好用的cdn》—— By hojun CDN的全称是Content Delivery Network,即内容分发网络。CDN是构建在网络之上的内容分发网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率。CDN的关键技术主要有内容存储和分发技术。——百度百科 放在Github的资源在国内加载速度比较慢,因此需要使用CDN加速来优化网站打开速度,jsDelivr + Github便是免费且好用的CDN,非常适合博客网站使用。 1、新建Github仓库 2、克隆Github仓库到本地 点击 Clone or download,一键复制仓库地址 在本地目录右键 Git Bash Here,执行以下命令: 1git clone 一键复制的仓库地址 3、上传资源 复制需要上传的资源到本地git仓库(注:jsDelivr不支持加载超过20M的资源),在本地git仓库目录下右键 Git Bash Here,执行以下命令:1234git status //查看状态git add . //添加所有文件到暂存区git commit -m '第一次提交' //把文件提交到仓库git push //推送至远程仓库 4、发布仓库 点击release发布 自定义发布版本号 5、通过jsDelivr引用资源 使用方法:https://cdn.jsdelivr.net/gh/你的用户名/你的仓库名@发布的版本号/文件路径例如:https://cdn.jsdelivr.net/gh/TRHX/CDN-for-itrhx.com@1.0/images/trhx.png    https://cdn.jsdelivr.net/gh/TRHX/CDN-for-itrhx.com@2.0.1/css/style.css    https://cdn.jsdelivr.net/gh/moezx/cdn@3.1.3//The%20Pet%20Girl%20of%20Sakurasou.mp4 注意:版本号不是必需的,是为了区分新旧资源,如果不使用版本号,将会直接引用最新资源,除此之外还可以使用某个范围内的版本,查看所有资源等,具体使用方法如下: // 加载任何Github发布、提交或分支https://cdn.jsdelivr.net/gh/user/repo@version/file // 加载 jQuery v3.2.1https://cdn.jsdelivr.net/gh/jquery/jquery@3.2.1/dist/jquery.min.js // 使用版本范围而不是特定版本https://cdn.jsdelivr.net/gh/jquery/jquery@3.2/dist/jquery.min.jshttps://cdn.jsdelivr.net/gh/jquery/jquery@3/dist/jquery.min.js // 完全省略该版本以获取最新版本https://cdn.jsdelivr.net/gh/jquery/jquery/dist/jquery.min.js // 将“.min”添加到任何JS/CSS文件中以获取缩小版本,如果不存在,将为会自动生成https://cdn.jsdelivr.net/gh/jquery/jquery@3.2.1/src/core.min.js // 在末尾添加 / 以获取资源目录列表https://cdn.jsdelivr.net/gh/jquery/jquery/","categories":[{"name":"CDN","slug":"CDN","permalink":"https://www.itrhx.com/categories/CDN/"}],"tags":[{"name":"jsDelivr","slug":"jsDelivr","permalink":"https://www.itrhx.com/tags/jsDelivr/"},{"name":"CDN","slug":"CDN","permalink":"https://www.itrhx.com/tags/CDN/"}]},{"title":"新年快乐!","slug":"A17-happy-new-year","date":"2019-02-04T19:09:51.832Z","updated":"2019-09-09T14:11:08.281Z","comments":true,"path":"2019/02/05/A17-happy-new-year/","link":"","permalink":"https://www.itrhx.com/2019/02/05/A17-happy-new-year/","excerpt":"","text":"C printf("2019,祝大家"); C++ cout<<"一帆风顺"; C# System.Console.WriteLine("二龙腾飞") VB Msg("三羊开泰") VC MessageBox("四季平安"); Java System.out.println("五福临门"); JavaScript alert("六六大顺") PHP echo "七星高照"; Python print("八方来财") Html <br/>九运当头<br/> Objectivec NSLog(@"十全十美"); QBasic Print "阖家幸福" Asp Response.Write "心想事成" Ruby puts "财源广进" VBScript MsgBox "幸福安康" XML <TextView android:text="大展宏图" /> LUA print("学业有成") Delphi ShowMessage('万事如意'); shell echo 步步高升 perl print '鸿案齐眉' LISP (format t "身体健康~%") powerBuilder messagebox("龙马精神") COBOL DISPLAY '笑口常开' aswing JOptionPane.showMessageDialog("happy","好运连连") Android Toast.makeText(getApplicationContext(),"年年有余",Toast.LENGTH_SHORT).show() flex Alert.show("大吉大利"); Foxpro ?[家庭幸福!] iapp tw("瑞气盈门") DOS批处理 echo 鹏程万里 易语言 调试输出(“万事亨通”) Clojure (println "年年有今昔") verilog/systemverilog/e $display("岁岁有今朝") as trace("祝大家新年快乐!");","categories":[{"name":"BLOG","slug":"BLOG","permalink":"https://www.itrhx.com/categories/BLOG/"}],"tags":[{"name":"BLOG","slug":"BLOG","permalink":"https://www.itrhx.com/tags/BLOG/"}]},{"title":"一台电脑使用两个/多个GitHub账号部署两个/多个Hexo博客","slug":"A16-deploy-two-or-more-hexo-blogs","date":"2019-01-18T13:23:24.345Z","updated":"2020-02-07T07:30:20.946Z","comments":true,"path":"2019/01/18/A16-deploy-two-or-more-hexo-blogs/","link":"","permalink":"https://www.itrhx.com/2019/01/18/A16-deploy-two-or-more-hexo-blogs/","excerpt":"由于个人原因需要在一台电脑上部署两个Hexo博客,本来以为挺简单,没想到问题重重,首先是一个GitHub账号只能搭建一个Hexo博客,因此就需要使用其他GitHub账号;其次是一台电脑绑定两个GitHub账号,则需要两对公钥,在处理第二个问题时遇到的问题比较多,因为对这方面一窍不通,还是小白,所以折腾了一下午才解决,网上好多教程我都看不懂,觉得不(自)够(己)详(太)细(笨),因此详细记录一下","text":"由于个人原因需要在一台电脑上部署两个Hexo博客,本来以为挺简单,没想到问题重重,首先是一个GitHub账号只能搭建一个Hexo博客,因此就需要使用其他GitHub账号;其次是一台电脑绑定两个GitHub账号,则需要两对公钥,在处理第二个问题时遇到的问题比较多,因为对这方面一窍不通,还是小白,所以折腾了一下午才解决,网上好多教程我都看不懂,觉得不(自)够(己)详(太)细(笨),因此详细记录一下 原理分析: SSH的公钥是GitHub作为本地仓库和远程仓库连接的唯一标识,一个公钥只能对应一个GitHub账户,如果将一个相同的公钥上传到不同的GitHub账户,GitHub则无法做出辨识,进而导致错误 一台电脑,可以生成多对公私钥,可以通过配置,将不同的公钥上传到不同的GitHub账号,那么就不存在单个公钥绑定多个GitHub账号的情况存在了 相关问题报错: 同一台电脑部署第二个Hexo博客执行hexo g -d时报错:ERROR: Permission to xxxxxx/xxxxxx.github.io.git denied to xxxxxx. 添加新的 SSH 密钥 到 SSH agent 执行ssh-add xxx时报错:Could not open a connection to your authentication agent. 单独设置用户名/邮箱时报错:fatal: not in a git directory 以下是详细过程:前提:假设你的第二个博客相关配置操作已经顺利完成,但使用hexo g -d命令部署到 GitHub 上时报错:ERROR: Permission to xxxxxx/xxxxxx.github.io.git denied to xxxxxx. - 查看当前密钥首先我们打开终端输入ls ~/.ssh/可以查看当前已有的密钥,显示id_rsa 与 id_rsa_pub说明已经有一对密钥 - 创建新的密钥首先使用以下命令进入 SSH根目录下:1cd ~/.ssh/ 方法一直接使用以下命令创建新密钥,然后两次回车即可:1ssh-keygen -t rsa -f ~/.ssh/这里是新密钥名称 -C \"这里是你的邮箱\" 注意区别新密钥名称和旧密钥名称,不要相同!!! 方法二使用下面命令行创建新密钥:1ssh-keygen -t rsa -C \"这里是你的邮箱\" 回车后会出现:12Generating public/private rsa key pair. Enter file in which to save the key (/c/Users/you/.ssh/id_rsa): 注意此时需要你输入新密钥的名称,同样要注意区别新密钥名称和旧密钥名称,不要相同!!!之后再两次回车,新密钥创建完毕! - 配置config查看你的.ssh/根路径下, 有没有config文件,( 比如我的路径为C:\\Users\\Lenovo.ssh)没有则使用以下命令创建一个config文件:1touch config 用记事本或者其他工具打开config文件(注意config文件是没有任何后缀名的),写入以下配置: 1234567891011#第一个账号,默认使用的账号,不用做任何更改Host github.com HostName github.com User git IdentityFile ~/.ssh/id_rsa #第二个新账号,#\"xxxxxx\"为前缀名,可以任意设置,要记住,后面需要用到Host xxxxxx.github.com HostName github.com User git IdentityFile ~/.ssh/这里是你创建的新密钥的名称 - 设置新GitHub账户SSH key输入以下命令复制你创建的公钥:1clip < ~/.ssh/这里是你创建的新密钥的名称.pub 也可以直接在.ssh目录下找到你创建的新的公钥,文件名为新密钥的名称.pub,(比如我的是trhx_rsa.pub),用记事本打开,复制里面的内容,然后打开你的新GitHub账号主页,依次进入Settings —> SSH and GPG keys —> New SSH key,将刚复制的内容粘贴到Key那里,Title可以随便填,点击Add Key保存。 - 清空本地的 SSH 缓存,添加新的 SSH 密钥 到 SSH agent中使用命令cd ~/.sshcd到.ssh根目录下,依次执行以下命令: 123ssh-add -Dssh-add xxxxxx #旧密钥名称,一般是id_rsassh-add xxxxxx #新创建的密钥名称 如果执行以上命令出现错误:Could not open a connection to your authentication agent.,那么就需要先执行ssh-agent bash,再执行以上命令 - 验证配置是否成功依次执行以下命令,第一个为默认ssh_key验证;第二个为新的ssh_key验证,其中“xxxxxx”为你先前在config文件中的命名12ssh -T git@github.comssh -T git@xxxxxxx.github.com 依次显示以下信息, 则说明配置成功:1Hi 你的用户名! You've successfully authenticated, but GitHub does not provide shell access. - 取消全局用户名/邮箱配置,单独设置用户名/邮箱执行如下命令,取消全局用户名和邮箱配置(如果已经设置了全局的话): 12git config --global --unset user.namegit config --global --unset user.email 分别进入你的两个Hexo博客.git目录下执行以下命令单独设置用户名/邮箱:12git config user.name \"这里是用户名\"git config user.email \"这里是你的邮箱\" 如果此时报错:fatal: not in a git directory,说明你没有进入.git目录下,具体路径:\\Hexo\\.deploy_git\\.git,.git目录是隐藏的,需要你设置隐藏目录可见 执行以下命令可以查看设置是否成功1git config --list - hexo 配置文件修改git地址打开你的第二个博客Hexo目录下的_config.yml文件,找到deploy关键字,写入以下配置并保存:1234deploy: type: git repository: git@xxxxxx.github.com:你的用户名/你的用户名.github.io.git branch: master 比如我的配置:1234deploy: type: git repository: git@love109.github.com:love109/love109.github.io.git branch: master 大功告成,再次执行hexo g -d就能成功将新的博客部署到 Github 上了","categories":[{"name":"Hexo","slug":"Hexo","permalink":"https://www.itrhx.com/categories/Hexo/"}],"tags":[{"name":"Hexo","slug":"Hexo","permalink":"https://www.itrhx.com/tags/Hexo/"},{"name":"Github","slug":"Github","permalink":"https://www.itrhx.com/tags/Github/"}]},{"title":"Python3 基础学习笔记 C09","slug":"A15-Python3-basic-C09","date":"2018-11-15T16:46:13.649Z","updated":"2019-09-24T12:45:51.693Z","comments":true,"path":"2018/11/16/A15-Python3-basic-C09/","link":"","permalink":"https://www.itrhx.com/2018/11/16/A15-Python3-basic-C09/","excerpt":"Python3 基础学习笔记第九章 —— 【文件和异常】","text":"Python3 基础学习笔记第九章 —— 【文件和异常】 - 9.1 从文件中读取数据 - 9.1.1 读取整个文件 有一个文件,包含精确到小数点后30位的圆周率值,且在小数点后每10位处都换行:12345Circumference rate.txt----------3.1415926535 8979323846 2643383279 以下两个程序将打开并读取这个文件,再将其内容显示到屏幕上:12345#file_reader.pywith open('Circumference rate.txt') as file_object: contents = file_object.read() print(contents) 12345#file_reader2.pycontents = open ('Circumference rate.txt')print(contents.read())contents.close() 函数open()接受一个参数:要打开的文件的名称,Python在当前执行的文件所在的目录中查找指定的文件;关键字with在不再需要访问文件后将其关闭;也可以调用open()和close()来打开和关闭文件,如果使用这种方法,当程序存在bug时,close()语句未执行,文件将不会被关闭;方法read()将读取这个文件的全部内容,并将其作为一个长长的字符串储存在变量contents中,通过打印contents的值,就可以将这个文本文件的全部内容打印出来:1233.1415926535 8979323846 2643383279 输出结果末尾有一空行,这是因为read()到达末尾时返回一个空字符串,而将这个空字符串显示出来就是一个空行,如果要删除末尾的空行,可在print语句中使用rstrip():12345#file_reader.pywith open('Circumference rate.txt') as file_object: contents = file_object.read() print(contents.rstrip()) 输出结果如下:1233.1415926535 8979323846 2643383279 - 9.1.2 文件路径 相对文件路径:假定程序文件位于python_work文件夹中,程序文件操作的文本文件位于python_work文件夹的子文件夹text_files中,此时可以使用相对文件路径来打开该文本文件,相对文件路径让Python到指定的位置去查找,而该位置是相对于当前运行的程序所在目录的 在Linux和OS X中,相对路径类似于如下:1with open('text_files/filename.txt') as file_object: 在Windows系统中,文件路径中使用反斜杠(\\)而不是斜杠(/):1with open('text_files\\filename.txt') as file_object: 绝对文件路径:不用关心当前运行的程序储存在什么地方,直接将文件在计算机中的准确位置告诉Python,这称为绝对文件路径,绝对路径通常比相对路径更长,因此将其储存在一个变量中,再将变量传递给open()会有所帮助 在Linux和OS X中,绝对路径类似于如下:12file_path = '/home/ehmatthes/other_files/text_files/filename.txt'with open(file_path) as file_object: 在Windows系统中,绝对路径类似于如下:12file_path = 'C:\\Users\\ehmatthes\\other_files\\text_files\\filename.txt'with open(file_path) as file_object: - 9.1.3 逐行读取 要以每次一行的方式检查文件,可对文件对象使用for循环:123456#file_reader.pyfilename = 'Circumference rate.txt'with open(filename) as file_object: for line in file_object: print(line) 在文件中每行的末尾都有一个看不见的换行符,而print语句也会加上一个换行符,因此每行末尾都有两个换行符:一个来自文件,一个来自print语句,输出结果如下:123453.1415926535 8979323846 2643383279 要消除这些多余的空白行,可以使用rstrip():123456#file_reader.pyfilename = 'Circumference rate.txt'with open(filename) as file_object: for line in file_object: print(line.rstrip()) 输出结果如下:1233.1415926535 8979323846 2643383279 - 9.1.4 创建一个包含文件各行内容的列表 使用关键字with时,open()返回的文件对象只在with代码块内可用,如果要在with代码块外访问文件的内容,可在with代码块内将文件的各行储存在一个列表当中,并在with代码块外使用该列表:12345678#file_reader.pyfilename = 'Circumference rate.txt'with open(filename) as file_object: lines = file_object.readlines() for line in lines: print(line.rstrip()) 输出结果与文件内容完全一致 - 9.1.5 使用文件的内容 创建一个字符串,它包含文件中储存的所有数字,且没有任何空格:123456789101112#pi_string.pyfilename = 'Circumference rate.txt'with open(filename) as file_object: lines = file_object.readlines()pi_string = ''for line in lines: pi_string += line.rstrip() print(pi_string)print(len(pi_string)) 打印该字符串以及其长度:123.1415926535 8979323846 264338327936 由于原文件每行左边都有空格,我们可以使用strip()而不是rstrip()来删除它:123456789101112#pi_string.pyfilename = 'Circumference rate.txt'with open(filename) as file_object: lines = file_object.readlines()pi_string = ''for line in lines: pi_string += line.strip() print(pi_string)print(len(pi_string)) 输出结果如下:123.14159265358979323846264338327932 Python中有三个去除头尾字符、空白符的函数,它们依次为: strip:用来去除头尾字符、空白符(包括\\n、\\r、\\t、’ ‘,即:换行、回车、制表符、空格) lstrip:用来去除开头字符、空白符(包括\\n、\\r、\\t、’ ‘,即:换行、回车、制表符、空格) rstrip:用来去除结尾字符、空白符(包括\\n、\\r、\\t、’ ‘,即:换行、回车、制表符、空格)注意:这些函数都只会删除头和尾的字符,中间的不会删除。用法分别为:string.strip([chars])string.lstrip([chars])string.rstrip([chars])参数chars是可选的,当chars为空,默认删除string头尾的空白符(包括\\n、\\r、\\t、’ ‘)当chars不为空时,函数会被chars解成一个个的字符,然后将这些字符去掉它返回的是去除头尾字符(或空白符)的string副本,string本身不会发生改变 - 9.2 写入文件 将一条简单的消息储存到文件中:12345#write_message.pyfilename = 'programming.txt'with open(filename,'w') as file_object: file_object.write(\"I love programming!\") 调用open()时提供了两个实参,第一个实参也是要打开文件的名称,第二个实参(’w’)告诉Python,我们要以写入模式打开这个文件,打开文件时,可指定读取模式(’r’)、写入模式(’w’)、附加模式(’a’)或者让我们能够读取和写入文件的模式(’r+’),如果省略模式实参,则默认以只读模式打开文件 附表:Python读写文件各种模式区别 模式 可做操作 若文件不存在 是否覆盖 r 打开一个文件用于只读 报错 - rb 以二进制格式打开一个文件用于只读 报错 - r+ 打开一个文件用于读和写 报错 是 rb+ 以二进制格式打开一个文件用于读和写 报错 是 w 打开一个文件用于只写 创建 是 wb 以二进制格式打开一个文件只用于只写 创建 是 w+ 打开一个文件用于读和写 创建 是 wb+ 以二进制格式打开一个文件用于读和写 创建 是 a 打开一个文件用于追加 创建 否,追加写 ab 以二进制格式打开一个文件用于追加 创建 否,追加写 a+ 打开一个文件用于读和写 创建 否,追加写 ab+ 以二进制格式打开一个文件用于追加 创建 否,追加写 - 9.3 使用 try-except 代码块处理异常 当我们尝试将一个数字除以0时,会发生ZeroDivisionError异常:12345>>> print(5/0)Traceback (most recent call last): File \"<pyshell#0>\", line 1, in <module> print(5/0)ZeroDivisionError: division by zero 此时我们可以编写一个try-except代码块来处理该异常:1234try: print(5/0)except ZeroDivisionError: print(\"You can't divide by zero!\") 当我们运行该程序时,会出现提示:1You can't divide by zero! 在try-except代码块中加入else,编写一个只执行除法运算的简单计算器:12345678910111213141516print(\"Give me two numbers,and I'll divide them.\")print(\"Enter 'q' to quit.\")while True: first_number = input(\"\\nFirst number:\") if first_number == 'q': break second_number = input(\"\\nSecond number:\") if second_number == 'q': break try: answer = int(first_number)/int(second_number) except ZeroDivisionError: print(\"You can't divide by 0!\") else: print(answer) 运行程序:1234567891011121314Give me two numbers,and I'll divide them.Enter 'q' to quit.First number:45Second number:0You can't divide by 0!First number:36Second number:84.5First number:q 若不加入try-except代码块,我们在输入0时,程序就会出现异常而崩溃,而try-except代码块很好的解决了这种问题,而且还起到了提示的作用,同样的,try-except代码块也可以处理其他异常,如FileNotFoundError等 - 9.4 储存数据 - 9.4.1 使用 json.dump() 和 json.load() 模块json能够将简单的Python数据结构转储到文件中,并在程序再次运行时加载该文件中的数据;编写一个储存一组数字的简短程序,再编写一个将这些数字读取到内存中的程序,第一个程序将使用 json.dump()来储存这组数据,而第二个程序将使用 json.load()。函数 json.dump()接受两个实参:要储存的数据以及可用于储存数据的文件对象:123456789#number_writer.pyimport jsonnumbers = [2,3,5,7,11,13]filename = 'numbers.json'with open(filename,'w') as f_obj: json.dump(numbers,f_obj) 先导入模块json,再创建一个数字列表, 通常用文件扩展名.json来指出文件储存的数据为JSON格式,然后以写入模式打开该文件,使用函数json.dump()将数字列表储存到文件numbers.json中,打开该文件,数据的储存格式与Python一样:1[2, 3, 5, 7, 11, 13] 再编写一个程序,使用json.load()将这个列表读取到内存中:12345678#number_reader.pyimport jsonfilename = 'numbers.json'with open(filename) as f_obj: numbers = json.load(f_obj)print(numbers) 输出结果与number_writer.py中创建的数字列表相同:1[2, 3, 5, 7, 11, 13] 进阶:在同一个程序中使用 json.dump() 和 json.load():创建文件username.json储存用户名,从该文件中获取用户名,如果这个文件不存在,就在except代码块中提示用户输入用户名,并将其储存在username.json中:1234567891011121314151617#remember_me.pyimport json#如果以前储存了用户名,就加载它#否则就提示用户输入用户名并储存它filename = 'numbers.json'try: with open(filename) as f_obj: username = json.load(f_obj)except FileNotFoundError: username = input(\"What's your name?\") with open(filename,'w') as f_obj: json.dump(username,f_obj) print(\"We'll remember you when you come back, \" + username + \"!\")else: print(\"Welcome back, \" + username + \"!\") 以前没有储存用户名,第一次运行程序:12What's your name?TRHXWe'll remember you when you come back, TRHX! 再次运行程序:1Welcome back, TRHX! - 9.4.2 重构 代码能够正确运行,但可以做进一步的改进——将代码划分为一系列完成具体工作的函数,这样的过程称为重构,重构让代码更清晰、更易于理解、更容易扩展重构remember_me.py,将大部分逻辑放到一个或者多个函数中:12345678910111213141516171819#remember_me.pyimport jsondef greet_user(): #问候用户,并指出其名字 filename = 'numbers.json' try: with open(filename) as f_obj: username = json.load(f_obj) except FileNotFoundError: username = input(\"What's your name?\") with open(filename,'w') as f_obj: json.dump(username,f_obj) print(\"We'll remember you when you come back, \" + username + \"!\") else: print(\"Welcome back, \" + username + \"!\")greet_user() 重构greet_user(),让它不执行这么多任务——将获取储存的用户名的代码移到另一个函数中:12345678910111213141516171819202122232425262728#remember_me.pyimport jsondef get_stored_username(): #如果储存了用户名,就获取它 filename = 'numbers.json' try: with open(filename) as f_obj: username = json.load(f_obj) except FileNotFoundError: return None else: return usernamedef greet_user(): #问候用户,并指出其名字 username = get_stored_username() if username: print(\"Welcome back, \" + username + \"!\") else: username = input(\"What's your name?\") filename = 'username.json' with open(filename,'w') as f_obj: json.dump(username,f_obj) print(\"We'll remember you when you come back, \" + username + \"!\") greet_user() 将greet_user()中的另一个代码块提取出来:将没有储存用户名时提示用户输入的代码放在一个独立的函数中:12345678910111213141516171819202122232425262728293031323334#remember_me.pyimport jsondef get_stored_username(): #如果储存了用户名,就获取它 filename = 'numbers.json' try: with open(filename) as f_obj: username = json.load(f_obj) except FileNotFoundError: return None else: return usernamedef get_new_username(): #提示输入用户名 username = input(\"What's your name?\") filename = 'username.json' with open(filename,'w') as f_obj: json.dump(username,f_obj) return username def greet_user(): #问候用户,并指出其名字 username = get_stored_username() if username: print(\"Welcome back, \" + username + \"!\") else: username = get_new_username() print(\"We'll remember you when you come back, \" + username + \"!\") greet_user() 最终版本实现了每个函数只负责单一而清晰的任务,我们在编写程序时也要像这样,要写出清晰而易于维护和扩展的代码","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"基础学习","slug":"Python3-学习笔记/基础学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/基础学习/"}],"tags":[{"name":"文件","slug":"文件","permalink":"https://www.itrhx.com/tags/文件/"},{"name":"异常","slug":"异常","permalink":"https://www.itrhx.com/tags/异常/"}]},{"title":"Python3 基础学习笔记 C08","slug":"A14-Python3-basic-C08","date":"2018-11-10T16:10:47.892Z","updated":"2019-09-24T12:45:48.188Z","comments":true,"path":"2018/11/11/A14-Python3-basic-C08/","link":"","permalink":"https://www.itrhx.com/2018/11/11/A14-Python3-basic-C08/","excerpt":"Python3 基础学习笔记第八章 —— 【类】","text":"Python3 基础学习笔记第八章 —— 【类】 - 8.1 创建类和使用类 创建一个表示小狗的简单类Dog,根据Dog类创建的每个实例都将储存名字和年龄,赋予每条小狗蹲下(sit())和打滚(roll_over())的能力: 1234567891011121314class Dog(): def __init__(self,name,age): #初始化属性name和age self.name = name self.age = age def sit(self): #模拟小狗被命令时蹲下 print(self.name.title() + \" is now sitting.\") def roll_over(self): #模拟小狗被命令时打滚 print(self.name.title() + \" rolled over!\") 方法init():类中的函数称为方法,本例中方法init()是一个特殊的方法,每当我们根据Dog类创建新实例时,Python都会自动运行它,在方法的名称中,开头和结尾各有两个下划线,这是一种约定,避免Python默认方法与普通方法发生名称冲突,例子中将方法init()定义成了包含三个形参:self、name和age,在这个方法的定义中,形参self必不可少,还必须位于其他形参的前面,Python调用方法init()来创建Dog实例时,将自动传入实参self,每个与类相关联的方法调用都自动传递实参self,它是一个指向实例本身的引用,让实例能够访问类中的属性和方法,我们创建Dog实例时,Python将调用Dog类的方法init(),我们将通过实参向Dog()传递名字和年龄;self会自动传递,因此我们不需要传递它,每当我们根据Dog类创建实例时,都只需要给最后两个形参(name和age)提供值;定义的两个变量都有前缀self,以self为前缀的变量都可以供类中的所有方法使用,还可以通过类的任何实例来访问这些变量。self.name = name 获取储存在形参name中的值,并将其储存到变量name中,然后该变量被关联到当前创建的实例。self.age = age 的作用与此类似,像这样可通过实例访问的变量称为属性;Dog还定义了另外两种方法:sit() 和 roll_over() ,由于这些方法不需要额外的信息,如名字和年龄,因此它们只有一个形参self 在Python 2.7中创建类时,需要在括号内包含单词object:12class ClassName(object): ---snip--- - 8.2 根据类创建实例访问属性:创建一个表示特定小狗的实例: 123456789101112131415161718class Dog(): def __init__(self,name,age): #初始化属性name和age self.name = name self.age = age def sit(self): #模拟小狗被命令时蹲下 print(self.name.title() + \" is now sitting.\") def roll_over(self): #模拟小狗被命令时打滚 print(self.name.title() + \" rolled over!\")my_dog = Dog('willie',6)print(\"My dog's name is \" + my_dog.name.title() + \".\")print(\"My dog is \" + str(my_dog.age) + \" years old.\") 让Python创建一条名字为’willie’,年龄为6的小狗,Python使用实参’willie’和6调用Dog类中的方法init()。方法init()创建一个表示特定小狗的示例,并使用我们提供的值来设置属性name和age;在访问实例的属性时,可使用句点表示法,比如该例子中的 my_dog.name;最终程序输出结果如下: 12My dog's name is Willie.My dog is 6 years old. 调用方法:根据Dog类创建实例后,就可以使用句点表示法来调用Dog类中定义的任何方法:123456789101112131415161718class Dog(): def __init__(self,name,age): #初始化属性name和age self.name = name self.age = age def sit(self): #模拟小狗被命令时蹲下 print(self.name.title() + \" is now sitting.\") def roll_over(self): #模拟小狗被命令时打滚 print(self.name.title() + \" rolled over!\")my_dog = Dog('willie',6)my_dog.sit()my_dog.roll_over() 输出结果如下:12Willie is now sitting.Willie rolled over! 创建多个实例:可按需求根据类创建任意数量的实例:12345678910111213141516171819202122232425class Dog(): def __init__(self,name,age): #初始化属性name和age self.name = name self.age = age def sit(self): #模拟小狗被命令时蹲下 print(self.name.title() + \" is now sitting.\") def roll_over(self): #模拟小狗被命令时打滚 print(self.name.title() + \" rolled over!\")my_dog = Dog('willie',6)your_dog = Dog('lucy',8)print(\"My dog's name is \" + my_dog.name.title() + \".\")print(\"My dog is \" + str(my_dog.age) + \" years old.\")my_dog.sit()print(\"\\nYour dog's name is \" + your_dog.name.title() + \".\")print(\"Your dog is \" + str(your_dog.age) + \" years old.\")your_dog.roll_over() 输出结果如下:1234567My dog's name is Willie.My dog is 6 years old.Willie is now sitting.Your dog's name is Lucy.Your dog is 8 years old.Lucy rolled over! - 8.3 使用类和实例 创建一个表示汽车的类,其中储存了有关汽车的信息,还有一个汇总这些信息的方法:12345678910111213class Car(): def __init__(self,make,model,year): self.make = make self.model = model self.year = year def get_descriptive_name(self): long_name = str(self.year) + ' ' + self.make + ' ' +self.model return long_name.title() my_new_car = Car('audi','a9','2018')print(my_new_car.get_descriptive_name()) 输出结果如下:12018 Audi A9 - 8.3.1 给属性指定默认值 类中的每个属性都必须有初始值,如果我们设置了默认值,就无需包含为它提供初始值的形参,下面为8.3的例子添加一个 odometer_reading 的属性,其初值是0,添加一个 odometer_reading() 方法,用于读取汽车的里程表: 123456789101112131415161718 class Car(): def __init__(self,make,model,year): self.make = make self.model = model self.year = year self.odometer_reading = 0 def get_descriptive_name(self): long_name = str(self.year) + ' ' + self.make + ' ' +self.model return long_name.title() def read_odomter(self): print(\"This car has \" + str(self.odometer_reading) + \" miles on it.\")my_new_car = Car('audi','a9','2018')print(my_new_car.get_descriptive_name())my_new_car.read_odomter() 输出结果如下:122018 Audi A9This car has 0 miles on it. - 8.3.2 修改属性的值 可以以三种不同的方式修改属性的值:直接通过实例进行修改;通过方法进行设置;通过方法进行递增(增加特定的值) 直接修改属性的值:要修改属性的值,最简单的方法就是通过实例直接访问它,将8.3.1中的例子第7行代码 self.odometer_reading = 0 改为 self.odometer_reading = 66,输出结果如下:12 2018 Audi A9This car has 66 miles on it. 通过方法修改属性的值:1234567891011121314151617181920212223class Car(): def __init__(self,make,model,year): self.make = make self.model = model self.year = year self.odometer_reading = 0 def get_descriptive_name(self): long_name = str(self.year) + ' ' + self.make + ' ' +self.model return long_name.title() def read_odomter(self): print(\"This car has \" + str(self.odometer_reading) + \" miles on it.\") def update_odometer(self,mileage): self.odometer_reading = mileage my_new_car = Car('audi','a9','2018')print(my_new_car.get_descriptive_name())my_new_car.update_odometer(66)my_new_car.read_odomter() 对Car类所做的唯一修改就是在第17、18行添加了方法 update_odometer(),这个方法接受一个里程值,并将其储存到 self.odometer_reading 中,在倒数第二行,调用了 update_odometer(),并向它提供了一个实参(该实参对应于方法定义中的形参mileage),它将里程数设置为66,而方法 read_odomter() 打印该读数:122018 Audi A9This car has 66 miles on it. 可对方法 update_odometer() 进行扩展,使其能够在修改里程表读数时做一些额外的工作,添加一些逻辑,禁止任何人将里程表读数往回调:1234567891011121314151617181920212223242526class Car(): def __init__(self,make,model,year): self.make = make self.model = model self.year = year self.odometer_reading = 50 def get_descriptive_name(self): long_name = str(self.year) + ' ' + self.make + ' ' +self.model return long_name.title() def read_odomter(self): print(\"This car has \" + str(self.odometer_reading) + \" miles on it.\") def update_odometer(self,mileage): if mileage >= self.odometer_reading: self.odometer_reading = mileage else: print(\"You can't roll back an odometer!\") my_new_car = Car('audi','a9','2018')print(my_new_car.get_descriptive_name())my_new_car.update_odometer(33)my_new_car.read_odomter() 修改 self.odometer_reading 的默认值为50,当我们再次尝试修改其值为33时,由于小于原来的里程,因此无法修改: 1232018 Audi A9You can't roll back an odometer!This car has 50 miles on it. 通过方法对属性的值进行递增:有时候需要将属性值递增到特定的量,而不是将其设置为全新的值,假设我们购买了一辆二手车,从购买到登记期间增加了100英里的里程,下面的方法让我们能够传递这个增量,并相应地增加里程表读数:123456789101112131415161718192021222324252627282930313233class Car(): def __init__(self,make,model,year): self.make = make self.model = model self.year = year self.odometer_reading = 0 def get_descriptive_name(self): long_name = str(self.year) + ' ' + self.make + ' ' +self.model return long_name.title() def read_odomter(self): print(\"This car has \" + str(self.odometer_reading) + \" miles on it.\") def update_odometer(self,mileage): if mileage >= self.odometer_reading: self.odometer_reading = mileage else: print(\"You can't roll back an odometer!\") def increment_odometer(self,miles): #将里程表读数增加指定的量 self.odometer_reading += miles my_new_car = Car('audi','a9','2018')print(my_new_car.get_descriptive_name())my_new_car.update_odometer(6600)my_new_car.read_odomter()my_new_car.increment_odometer(100)my_new_car.read_odomter() 输出结果如下:1232018 Audi A9This car has 6600 miles on it.This car has 6700 miles on it. - 8.4 继承 编写类时,并非总是要从空白开始,如果要编写的类是另一个现成类的特殊版本,可使用继承,一个类继承另一个类时,它自动获得另一个类的所有属性和方法;原有的类称为父类,而新类称为子类,子类继承了其父类的所有属性和方法,同时还可以定义自己的属性和方法;继承的通用语法大致如下: 12345678class ClassName1(object): def __init__(self,name1,name2,name3): --snip--class ClassName2(ClassName1): def __init__(self,name1,name2,name3): super().__init__(name1,name2,name3) --snip-- - 8.4.1 子类的方法init() 1234567891011121314151617181920 class Car(): def __init__(self,make,model,year): self.make = make self.model = model self.year = year self.odometer_reading = 0 def get_descriptive_name(self): long_name = str(self.year) + ' ' + self.make + ' ' +self.model return long_name.title()class ElectricCar(Car): #电动车的独特之处 def __init__(self,make,model,year): #初始化父类的属性 super().__init__(make,model,year)my_new_car = ElectricCar('tesla','model s','2016')print(my_new_car.get_descriptive_name()) 创建子类时,父类必须包含在当前文件中,且位于子类前面,定义了子类 ElectricCar,定义子类时,必须在括号内指定父类名称,方法 __init__()接受创建Car实例所需信息,super() 是一个特殊的函数,帮助Python将父类和子类关联起来,让Python调用 ElectricCar 的父类的方法 __init__(),让 ElectricCar 实例包含父类的所有属性,父类也称为超类(superclass),程序输出结果如下:12016 Tesla Model S - 8.4.2 Python 2.7 中的继承 在Python 2.7中,ElectricCar类的定义类似于下面这样:12345678class Car(object): def __init__(self,make,model,year): --snip--class ElectricCar(Car): def __init__(self,make,model,year): super(ElectricCar,self).__init__(make,model,year) --snip-- - 8.4.3 给子类定义属性和方法 让一个类继承另一个类后,可添加区分子类和父类所需的新属性和方法,下面添加一个电动车特有的属性(battery),以及一个描述该属性的方法: 1234567891011121314151617181920212223class Car(): def __init__(self,make,model,year): self.make = make self.model = model self.year = year self.odometer_reading = 0 def get_descriptive_name(self): long_name = str(self.year) + ' ' + self.make + ' ' +self.model return long_name.title()class ElectricCar(Car): def __init__(self,make,model,year): super().__init__(make,model,year) self.battery_size = 80 def describe_battery(self): print(\"This car has a \" + str(self.battery_size) + \"-KWh battery.\")my_new_car = ElectricCar('tesla','model s','2016')print(my_new_car.get_descriptive_name())my_new_car.describe_battery() 输出结果如下:122016 Tesla Model SThis car has a 80-KWh battery. - 8.4.4 重写父类的方法 要重写父类的方法,只需要在子类中定义一个与要重写的父类方法同名的方法即可,这样,Python将不会考虑这个父类的方法,而只关心在子类中定义的相应方法,假设Car类有一个名为 fill_gas_tank() 的方法,对于电动车来说毫无意义,因此可以重写它:12345class ElectricCar(Car): --snip-- def fill_gas_tank(self): print(\"This car doesn't need a gas tank!\") - 8.4.5 将实例用作属性 123456789101112131415161718192021222324252627282930class Car(): def __init__(self,make,model,year): self.make = make self.model = model self.year = year self.odometer_reading = 0 def get_descriptive_name(self): long_name = str(self.year) + ' ' + self.make + ' ' +self.model return long_name.title()class Battery(): #一次模拟电动车电瓶的简单尝试 def __init__(self,battery_size=70): #初始化电瓶的属性 self.battery_size = battery_size def describe_battery(self): #打印一条描述电瓶容量的消息 print(\"This car has a \" + str(self.battery_size) + \"-KWh battery.\")class ElectricCar(Car): def __init__(self,make,model,year): super().__init__(make,model,year) self.battery = Battery()my_new_car = ElectricCar('tesla','model s','2016')print(my_new_car.get_descriptive_name())my_new_car.battery.describe_battery() 输出结果如下: 122016 Tesla Model SThis car has a 70-KWh battery. 看起来似乎做了多余的工作,但现在我们可以对电瓶添加更多的描述,而且不会导致 ElectricCar 类混乱不堪,下面再给Battery添加一个方法,使其能够根据电瓶容量报告汽车的续航里程:1234567891011121314151617181920212223242526272829303132333435363738394041class Car(): def __init__(self,make,model,year): self.make = make self.model = model self.year = year self.odometer_reading = 0 def get_descriptive_name(self): long_name = str(self.year) + ' ' + self.make + ' ' +self.model return long_name.title()class Battery(): #一次模拟电动车电瓶的简单尝试 def __init__(self,battery_size=70): #初始化电瓶的属性 self.battery_size = battery_size def describe_battery(self): #打印一条描述电瓶容量的消息 print(\"This car has a \" + str(self.battery_size) + \"-KWh battery.\") def get_range(self): #打印一条消息,指出电瓶的续航里程 if self.battery_size == 70: range = 240 elif self.battery_size == 90: range = 280 message = \"This car can go approximately \" + str(range) message += \" miles on a full charge.\" print(message) class ElectricCar(Car): def __init__(self,make,model,year): super().__init__(make,model,year) self.battery = Battery()my_new_car = ElectricCar('tesla','model s','2016')print(my_new_car.get_descriptive_name())my_new_car.battery.describe_battery()my_new_car.battery.get_range() 输出结果如下:1232016 Tesla Model SThis car has a 70-KWh battery.This car can go approximately 240 miles on a full charge. - 8.5 导入类 Python允许将类储存在模块中,然后在主程序中导入所需的模块 - 8.5.1 导入单个类 12345678910111213141516171819202122232425262728293031#car.py#一个用于表示汽车的类class Car(): def __init__(self,make,model,year): #初始化描述汽车的属性 self.make = make self.model = model self.year = year self.odometer_reading = 0 def get_descriptive_name(self): #返回整洁的描述性名称 long_name = str(self.year) + ' ' + self.make + ' ' +self.model return long_name.title() def read_odomter(self): #打印一条消息,指出汽车的里程 print(\"This car has \" + str(self.odometer_reading) + \" miles on it.\") def update_odometer(self): #将里程表读数设置为指定的值,拒绝将里程表往回拨 if mileage >= self.odometer_reading: self.odometer_reading = mileage else: print(\"You can't roll back an odometer!\") def increment_odometer(self,miles): #将里程表读数增加指定的量 self.odometer_reading += miles 创建另一个文件——my_car.py,在其中导入Car类并创建其实例:123456789#my_car.pyfrom car import Carmy_new_car = Car('audi','a9','2018')print(my_new_car.get_descriptive_name())my_new_car.odometer_reading = 23my_new_car.read_odometer() import语句让Python打开模块car,并导入其中的Car类,输出结果如下: 122018 Audi A9This car has 23 miles on it. - 8.5.2 在一个模块中储存多个类 将类Battery和ElectricCar都加入到模块car.py中: 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758#car.py#一组用于表示燃油汽车和电动汽车的类class Car(): def __init__(self,make,model,year): #初始化描述汽车的属性 self.make = make self.model = model self.year = year self.odometer_reading = 0 def get_descriptive_name(self): #返回整洁的描述性名称 long_name = str(self.year) + ' ' + self.make + ' ' +self.model return long_name.title() def read_odometer(self): #打印一条消息,指出汽车的里程 print(\"This car has \" + str(self.odometer_reading) + \" miles on it.\") def update_odometer(self): #将里程表读数设置为指定的值,拒绝将里程表往回拨 if mileage >= self.odometer_reading: self.odometer_reading = mileage else: print(\"You can't roll back an odometer!\") def increment_odometer(self,miles): #将里程表读数增加指定的量 self.odometer_reading += milesclass Battery(): #一次模拟电动车电瓶的简单尝试 def __init__(self,battery_size=70): #初始化电瓶的属性 self.battery_size = battery_size def describe_battery(self): #打印一条描述电瓶容量的消息 print(\"This car has a \" + str(self.battery_size) + \"-KWh battery.\") def get_range(self): #打印一条消息,指出电瓶的续航里程 if self.battery_size == 70: range = 240 elif self.battery_size == 90: range = 280 message = \"This car can go approximately \" + str(range) message += \" miles on a full charge.\" print(message)class ElectricCar(Car): #模拟电动车的独特之处 def __init__(self,make,model,year): #初始化父类的属性,再初始化电动车特有的属性 super().__init__(make,model,year) self.battery = Battery() 新建一个my_electric_car.py的文件,导入ElectricCar类,并创建一辆电动车:123456789#my_electric_car.pyfrom car import ElectricCarmy_tesla = ElectricCar('tesla','model s','2016')print(my_tesla.get_descriptive_name())my_tesla.battery.describe_battery()my_tesla.battery.get_range() 输出结果如下:1232016 Tesla Model SThis car has a 70-KWh battery.This car can go approximately 240 miles on a full charge. - 8.5.3 从一个模块中导入多个类 可根据需要在程序文件中导入任意数量的类,假如我们要在同一个程序中创建普通汽车和电动汽车,就需要将类Car和ElectricCar类都导入,多个类之间用逗号进行分隔: 123456789 #my_car.pyfrom car import Car,ElectricCarmy_audi = Car('audi','a9','2018')print(my_audi.get_descriptive_name())my_tesla = ElectricCar('tesla','model s','2016')print(my_tesla.get_descriptive_name()) 输出结果如下:122018 Audi A92016 Tesla Model S - 8.5.4 导入整个模块 导入整个模块后,需要使用句点表示法访问需要的类:123456789#my_car.pyimport carmy_audi = car.Car('audi','a9','2018')print(my_audi.get_descriptive_name())my_tesla = car.ElectricCar('tesla','model s','2016')print(my_tesla.get_descriptive_name()) 我们导入了整个car模块,需要使用语法 module_name.class_name 访问需要的类,程序输出结果与8.5.3一致:122018 Audi A92016 Tesla Model S - 8.5.5 导入模块中的所有类 要导入模块中的所有类,可使用以下语法:1from module_name import * 这种导入方法是不推荐的,没有明确指出你使用了模块中的哪些类,还可能引发名称方面的困惑,需要从一个模块中导入很多类时,最好导入整个模块,并使用 module_name.class_name 语法来访问类 - 8.5.6 在一个模块中导入另一个模块 有时候需要将类分散到多个模块当中,以免模块太大,或者在同一个模块中储存不相关的类,将类储存在多个模块中时,一个模块中的类可能会依赖于另一个模块中的类,这种情况下,我们可以在前一个模块中导入必要的类,以下例子中,将Car类储存在一个模块当中,并将ElectricCar和Battery类储存在另一个模块当中,将第二个模块命名为electric_car.py,并将ElectricCar和Battery类复制到这个模块中:12345678910111213141516171819202122232425262728293031#electric_car.py#一组可用于表示电动汽车的类from car import Carclass Battery(): #一次模拟电动车电瓶的简单尝试 def __init__(self,battery_size=70): #初始化电瓶的属性 self.battery_size = battery_size def describe_battery(self): #打印一条描述电瓶容量的消息 print(\"This car has a \" + str(self.battery_size) + \"-KWh battery.\") def get_range(self): #打印一条消息,指出电瓶的续航里程 if self.battery_size == 70: range = 240 elif self.battery_size == 90: range = 280 message = \"This car can go approximately \" + str(range) message += \" miles on a full charge.\" print(message)class ElectricCar(Car): #模拟电动车的独特之处 def __init__(self,make,model,year): #初始化父类的属性,再初始化电动车特有的属性 super().__init__(make,model,year) self.battery = Battery() 12345678910111213141516171819202122232425262728293031#car.py#一个可用于表示汽车的类class Car(): def __init__(self,make,model,year): #初始化描述汽车的属性 self.make = make self.model = model self.year = year self.odometer_reading = 0 def get_descriptive_name(self): #返回整洁的描述性名称 long_name = str(self.year) + ' ' + self.make + ' ' +self.model return long_name.title() def read_odometer(self): #打印一条消息,指出汽车的里程 print(\"This car has \" + str(self.odometer_reading) + \" miles on it.\") def update_odometer(self): #将里程表读数设置为指定的值,拒绝将里程表往回拨 if mileage >= self.odometer_reading: self.odometer_reading = mileage else: print(\"You can't roll back an odometer!\") def increment_odometer(self,miles): #将里程表读数增加指定的量 self.odometer_reading += miles 现在可以分别从每个模块中导入类:12345678910#my_car.pyfrom car import Carfrom electric_car import ElectricCarmy_audi = Car('audi','a9','2018')print(my_audi.get_descriptive_name())my_tesla = ElectricCar('tesla','model s','2016')print(my_tesla.get_descriptive_name()) 输出结果如下:122018 Audi A92016 Tesla Model S - 8.6 Python标准库 Python标准库是一组模块,安装的Python都包含它,我们可以使用标准库中的任何函数和类,只需要在程序的开头包含一条简单的import语句,下面以模块collections中的一个类——OrderedDict(创建字典并记录其中的键-值对的添加顺序)为例:1234567891011121314#favorite_languages.pyfrom collections import OrderedDictfavorite_languages = OrderedDict()favorite_languages ['jen'] = 'python'favorite_languages ['sarah'] = 'c'favorite_languages ['edward'] = 'java'favorite_languages ['anly'] = 'python'for name,language in favorite_languages.items(): print(name.title() + \"'s favorite languages is \" + language.title() + \".\") 输出结果如下:1234Jen's favorite languages is Python.Sarah's favorite languages is C.Edward's favorite languages is Java.Anly's favorite languages is Python.","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"基础学习","slug":"Python3-学习笔记/基础学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/基础学习/"}],"tags":[{"name":"类","slug":"类","permalink":"https://www.itrhx.com/tags/类/"},{"name":"继承","slug":"继承","permalink":"https://www.itrhx.com/tags/继承/"}]},{"title":"Python3 基础学习笔记 C07","slug":"A13-Python3-basic-C07","date":"2018-11-03T14:21:47.735Z","updated":"2019-09-24T12:45:42.627Z","comments":true,"path":"2018/11/03/A13-Python3-basic-C07/","link":"","permalink":"https://www.itrhx.com/2018/11/03/A13-Python3-basic-C07/","excerpt":"Python3 基础学习笔记第七章 —— 【函数】","text":"Python3 基础学习笔记第七章 —— 【函数】 - 7.1 定义函数 一个简单的函数,命名为 example(),其中,关键字 def 来告诉Python我们要定义一个函数,这就是函数定义 123def example(): print(\"Hello world!\")example() 输出结果如下: 1Hello world! - 7.1.1 向函数传递信息 在函数定义 def example() 的括号中添加 username,可以让函数接受我们给 username 指定的任何值,在调用函数时给 username 指定一个值,调用 example() 时,可将一个名字传递给它: 123def example(username): print(\"Hello , \" + username + '!')example('TRHX') 输出结果如下: 1Hello , TRHX! - 7.1.2 实参和形参 在 7.1.1 的例子中,函数 example() 的定义中,变量 username 是一个形参——函数完成其工作所需的一项信息,在代码 example(‘TRHX’) 中,值’TRHX’是一个实参,实参是调用函数时传递给函数的信息,调用函数时,将要让函数使用的信息放在括号内。在 example(‘TRHX’) 中,将实参 ‘TRHX’ 传递给了函数 example,这个值被储存在形参 username 中 - 7.2 传递实参 鉴于函数定义中可能包含多个形参,因此函数调用中也可能包含多个实参。向函数传递实参的方式很多,可使用位置实参,这要求实参的顺序与形参的顺序相同;也可以使用关键字实参,其中每个实参都由变量和值组成;还可以使用列表和字典 - 7.2.1 位置实参 调用函数时,Python必须将函数调用中的每个实参都关联到函数定义中的一个形参。为此,最简单的方法是基于实参的顺序,这种关联方式被称为位置实参 1234def describe_pet(animal_type , pet_name): print(\"I have a \" + animal_type + \".\") print(\"My \" + animal_type + \"'s name is \" + pet_name.title() + \".\")describe_pet('hamster' , 'harry') 输出结果如下: 12I have a hamster.My hamster's name is Harry. 调用函数多次:我们可以根据需要调用函数任意次,要再描述一个宠物,只需要再次调用 123456describe_pet() 即可def describe_pet(animal_type , pet_name): print(\"I have a \" + animal_type + \".\") print(\"My \" + animal_type + \"'s name is \" + pet_name.title() + \".\")describe_pet('hamster' , 'harry')describe_pet('dog' , 'willi') 输出结果如下: 1234I have a hamster.Myhamster's name is Harry.I have a dog.My dog's name is Willi. - 7.2.2 关键字实参 关键字实参是传递给函数的名称-值对。直接在实参中将名称和值关联起来,不用考虑函数调用中的实参顺序 12345def describe_pet(animal_type , pet_name): print(\"I have a \" + animal_type + \".\") print(\"My \" + animal_type + \"'s name is \" + pet_name.title() + \".\")describe_pet(animal_type = 'hamster' , pet_name = 'harry')describe_pet(pet_name = 'willi' , animal_type = 'dog' ) 输出结果如下: 1234I have a hamster.Myhamster's name is Harry.I have a dog.My dog's name is Willi. - 7.2.3 默认值 编写函数时,可给每个形参指定默认值,在调用函数中给形参提供了实参时,Python将使用指定的实参值,否则将使用形参的默认值 1234def describe_pet(pet_name , animal_type = 'dog'): print(\"I have a \" + animal_type + \".\") print(\"My \" + animal_type + \"'s name is \" + pet_name.title() + \".\")describe_pet(pet_name = 'willi') 输出结果如下: 12I have a dog.My dog's name is Willi. 在这个函数定义中,修改了形参的排列顺序,由于给 animal_type 指定了默认值,无需通过实参来指定动物类型,因此在函数调用中只包含一个实参——宠物的名字,然而Python依然将这个实参视为位置实参,因此如果函数调用中只包含宠物的名字,这个实参将关联到函数定义中的第一个形参,这就是需要将 pet_name 放在形参列表开头的原因所在 注意:使用默认值时,在形参列表中必须先列出没有默认值的形参,再列出有默认值的形参,这让Python依然能够准确地解读位置实参 - 7.3 返回值 函数并非总是直接显示输出,相反,它可以处理一些数据,并返回一个或一组值,函数返回的值被称为返回值,在函数中,可使用 return 语句将值返回到函数调用的代码行 - 7.3.1 返回简单值 12345def name(first_name , last_name): full_name = first_name + ' ' + last_name return full_name.title()student = name('jimi' , 'hendrix')print(student) 输出结果如下: 1Jimi Hendrix - 7.3.2 让实参变成可选的 对 7.3.1 的例子进行改进,扩展函数 name,使其还能够处理中间名: 12345def name(first_name , middle_name , last_name): full_name = first_name + ' ' + middle_name + ' ' + last_name return full_name.title()student = name('jimi' , 'lee' , 'hendrix')print(student) 输出结果如下: 1Jimi Lee Hendrix 然而,如果一个人没有中间名,那么在调用这个函数时就会出错,为了让中间名变成可选的,可以给实参 middle_name 指定一个默认值——空字符串,并在用户没有提供中间名时不使用这个实参,注意需要将 middle_name 移到形参列表的末尾: 12345678910def name(first_name , last_name , middle_name = ' '): if middle_name: full_name = first_name + ' ' + middle_name + ' ' + last_name else: full_name = first_name + ' ' + last_name return full_name.title()student = name('jimi' , 'hendrix')print(student)student = name('jimi' , 'hendrix' , 'lee' )print(student) 输出结果如下: 12Jimi HendrixJimi Lee Hendrix - 7.3.3 返回字典 函数可返回任何类型的值,包括列表和字典等较复杂的数据结构: 12345def name(first_name , last_name): full_name = {'first' : first_name , 'last' : last_name} return full_namestudent = name('jimi' , 'hendrix')print(student) 输出结果如下: 1{'first': 'jimi', 'last': 'hendrix'} - 7.3.4 结合使用函数和 while 循环 123456789101112131415def name(first_name , last_name): full_name = first_name + ' ' + last_name return full_namewhile True: print(\"\\nPlease input your name:\") print(\"(Enter 'exit' to quit)\") f_name = input(\"First_name:\") if f_name == 'exit': break l_name = input(\"Last_name:\") if l_name == 'exit': break student = name(f_name , l_name) print(student) print(\"Hello, \" + student.title() + \"!\") 运行程序: 1234567891011Please input your name:(Enter 'exit' to quit)First_name:jimiLast_name:hendrixjimi hendrixHello, Jimi Hendrix!Please input your name:(Enter 'exit' to quit)First_name:exit - 7.4 传递列表 123456def users(names): for name in names: message = \"Hello, \" + name.title() + \"!\" print(message)usernames = ['hannah' , 'tony' , 'margot']users(usernames) 输出结果如下: 123Hello, Hannah!Hello, Tony!Hello, Margot! - 7.4.1 在函数中修改列表 将列表传递给函数后,函数就可以对其进行修改,在函数中对这个列表所做的任何修改都是永久性的 #首先创造一个列表,其中包含一些要打印的设计 12345678910111213141516unprinted_designs = ['iphone case' , 'robot pendannt' , 'dodecahedron']completed_models = []#模拟打印每个设计,直到没有未打印的设计为止#打印每个设计后,都将其移到列表completed_models中while unprinted_designs: current_design = unprinted_designs.pop() #模拟根据设计制作3D打印模型的过程 print(\"Printing model: \" + current_design) completed_models.append(current_design) #显示打印好的所有模型print(\"\\nThe following models have been printed: \")for completed_model in completed_models: print(completed_model) 输出结果如下: 12345678Printing model: dodecahedronPrinting model: robot pendanntPrinting model: iphone caseThe following models have been printed: dodecahedronrobot pendanntiphone case 编写两个函数重新组织这些代码,每一个函数都做一件具体的工作,输出结果与原程序相同: 123456789101112131415161718192021def print_models(unprinted_designs , completed_models):#模拟打印每个设计,直到没有未打印的设计为止#打印每个设计后,都将其移到列表completed_models中 while unprinted_designs: current_design = unprinted_designs.pop() #模拟根据设计制作3D打印模型的过程 print(\"Printing model: \" + current_design) completed_models.append(current_design)def show_completed_models(completed_models): #显示打印好的所有模型 print(\"\\nThe following models have been printed: \") for completed_model in completed_models: print(completed_model)unprinted_designs = ['iphone case' , 'robot pendannt' , 'dodecahedron']completed_models = []print_models(unprinted_designs , completed_models)show_completed_models(completed_models) - 7.4.2 禁止函数修改列表 有时候需要禁止函数修改列表,拿 7.4.1 的例子来说,我们打印了所有设计后,也要保留原来的未打印的设计列表,以供备案,但由于我们将所有的设计都移出了 unprinted_designs,这个列表变成了空的,原来的列表没有了,为了解决这个问题,可向函数传递列表的副本而不是原件;这样函数所做的任何修改都只影响副本,而丝毫不影响原件,要将列表的副本传递给函数,可以像下面这样做: 1function_name(list_name[:]) 切片表示法 [:] 创建列表的副本,在 7.4.1 的例子中如果不想清空未打印的设计列表,可像下面这样调用 print_models(): 1print_models(unprinted_designs[:] , completed_models) - 7.5 传递任意数量的实参 Python允许函数从调用语句中收集任意数量的实参 1234def make_pizza(*toppings): print(toppings)make_pizza('pepperoni')make_pizza('mushrooms' , 'green peppers' , 'extra cheese') 形参名 *toppings 中的星号让Python创建一个名为 toppings 的空元组,并将收到的所有值都封装到这个元组中,函数体内的print语句通过生成输出来证明Python能够处理使用一个值调用函数的情形,也能处理使用三个值来调用函数的情形,输出结果如下: 12('pepperoni',)('mushrooms', 'green peppers', 'extra cheese') 使用循环语句: 123456def make_pizza(*toppings): print(\"\\nMaking a pizza with the followiing toppings: \") for topping in toppings: print(\"- \" + topping)make_pizza('pepperoni')make_pizza('mushrooms' , 'green peppers' , 'extra cheese') 输出结果如下: 12345678Making a pizza with the followiing toppings: - pepperoniMaking a pizza with the followiing toppings: - mushrooms- green peppers- extra cheese - 7.5.1 结合使用位置实参和任意数量实参 如果要让函数接受不同类型的实参,必须在函数定义中将接纳任意数量实参的形参放在最后。Python先匹配位置实参和关键字实参,再将余下的实参都收集到最后一个形参中: 123456def make_pizza(size , *toppings): print(\"\\nMaking a \" + str(size) + \"-inch pizza with the followiing toppings: \") for topping in toppings: print(\"- \" + topping)make_pizza(16 , 'pepperoni')make_pizza(18 , 'mushrooms' , 'green peppers' , 'extra cheese') 输出结果如下: 12345678Making a 16-inch pizza with the followiing toppings: - pepperoniMaking a 18-inch pizza with the followiing toppings: - mushrooms- green peppers- extra cheese - 7.5.2 使用任意数量的关键字实参 有时候,需要接受任何数量的实参,但预先我们不知道传递给函数的会是什么样的信息,在这种情况下,可以将函数编写成能够接受任意数量的键-值对——调用语句提供了多少就接受多少: 12345678910def build_profile(first , last , **user_info): #创建一个字典,其中包括我们知道的有关用户的一切 profile = {} profile['first_name'] = first profile['last_name'] = last for key , value in user_info.items(): profile[key] = value return profileuser_profile = build_profile('albert' , 'einstein' , location = 'princeton' , field = 'physics')print(user_profile) 形参 **user_info 中的两个星号让Python创建一个名为 user_info 的空字典,并将收到的所有名称-值对都封装到这个字典中,在这个函数中,可以像访问其他字典那样访问 user_info 中的名字-值对,程序运行结果如下: 1{'first_name': 'albert', 'last_name': 'einstein', 'location': 'princeton', 'field': 'physics'} - 7.6 将函数储存在模块中 更进一步,我们可以把函数储存在被称为模块的独立文件中,再将模块导入到主程序中,import 语句运行在当前运行的程序文件中使用模块中的代码 - 7.6.1 导入整个模块 要让函数是可导入的,得先创建模块,模块是扩展名为.py的文件,包含要导入到程序中的代码,下面将创建一个包含函数 make_pizza() 的模块 1234567#pizza.pydef make_pizza(size , *toppings): #概述要制作的比萨 print(\"\\nMaking a \" + str(size) + \"-inch pizza with the followiing toppings: \") for topping in toppings: print(\"- \" + topping) 接下来,我们在 pizza.py 所在的目录中创建另一个名为 making_pizzas.py 的文件,在这个文件中导入刚刚创建的模块,在调用 make_pizza() 两次: 12345#making_pizzas.pyimport pizzapizza.make_pizza(16 , 'pepperoni')pizza.make_pizza(18 , 'mushrooms' , 'green peppers' , 'extra cheese') Python在读取这个文件时,代码行 import pizza 让Python打开文件 pizza.py,并在幕后将其中所有函数都复制到这个程序中,在 making_pizzas.py 中,可以使用 pizza.py 中定义的所有函数,要调用被导入的模块中的函数,可指定导入的模块的名称 pizza 和函数名 make_pizza(),并使用句点分隔它们,最终运行结果与原程序相同: 12345678Making a 16-inch pizza with the followiing toppings: - pepperoniMaking a 18-inch pizza with the followiing toppings: - mushrooms- green peppers- extra cheese - 7.6.2 导入特定的函数 导入模块中特定的函数,可以使用以下语法: 1from module_name import function_name 通过用逗号分隔函数名,可根据需要从模块中导入任意数量的函数:1from module_name import function_0 , function_1 , function_2 以前面的 making_pizzas.py 为例,如果只想导入要使用的函数,代码类似于下面这样:1234from pizza import make_pizzamake_pizza(16 , 'pepperoni')make_pizza(18 , 'mushrooms' , 'green peppers' , 'extra cheese') - 7.6.3 使用 as 给函数指定别名 如果要导入的函数名称可能与程序中现有的名称冲突,或者函数的名称太长,可指定简短而独一无二的别名,要给函数指定别名,需要在导入它的时候这样做,通用语法为:1from module_name import function_name as fn 同样以前面的 making_pizzas.py 为例:1234from pizza import make_pizza as mpmp(16 , 'pepperoni')mp(18 , 'mushrooms' , 'green peppers' , 'extra cheese') - 7.6.4 使用 as 给模块指定别名 我们还可以给模块指定别名,通用语法为:1import module_name as mn 同样以前面的 making_pizzas.py 为例:1234import pizza as pp.make_pizza(16 , 'pepperoni')p.make_pizza(18 , 'mushrooms' , 'green peppers' , 'extra cheese') - 7.6.5 导入模块中的所有函数 导入模块中所有函数的通用语法为:1from module_name import * 同样以前面的 making_pizzas.py 为例:1234from pizza import *make_pizza(16 , 'pepperoni')make_pizza(18 , 'mushrooms' , 'green peppers' , 'extra cheese') import 语句中的星号让Python将模块 pizza 中的每个函数都复制到这个程序中,由于导入了每个函数,可通过名称来调用每个函数,而不需要用句点表示法,然而,如果模块中有函数的名称与项目中的名称相同,就有可能导致意想不到的结果,最佳的做法是,要么只导入我们需要使用的函数,要么导入整个模块并使用句点表示法","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"基础学习","slug":"Python3-学习笔记/基础学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/基础学习/"}],"tags":[{"name":"函数","slug":"函数","permalink":"https://www.itrhx.com/tags/函数/"},{"name":"模块","slug":"模块","permalink":"https://www.itrhx.com/tags/模块/"}]},{"title":"Python3 基础学习笔记 C06","slug":"A12-Python3-basic-C06","date":"2018-10-30T05:42:31.562Z","updated":"2019-09-24T12:45:39.386Z","comments":true,"path":"2018/10/30/A12-Python3-basic-C06/","link":"","permalink":"https://www.itrhx.com/2018/10/30/A12-Python3-basic-C06/","excerpt":"Python3 基础学习笔记第六章 —— 【用户输入和 while 循环】","text":"Python3 基础学习笔记第六章 —— 【用户输入和 while 循环】 - 6.1 函数 input() 的工作原理 函数 input() 让程序暂停运行,等待用户输入一些文本。获取用户输入后,Python将其储存在一个变量当中,以方便你使用;函数 input() 返回为 string 类型 12message = input(\"Please tell me your name:\")print(\"Hello , \" + message + \"!\") 输出结果如下: 12Please tell me your name:anliyHello , anliy! 进阶: 1234message = \"Please tell me your name so that we can personalize the messages you see.\"message += \"\\nWhat's your first name?\"name = input(message)print(\"\\nHello , \" + name + \"!\") 输出结果如下: 1234Please tell me your name so that we can personalize the messages you see.What's your first name?trhxHello , trhx! - 6.1.1 使用 int() 来获取数值输入 使用函数 input() 时,Python会将用户输入解读为字符串: 1234>>> age = input(\"How old are you?\")How old are you?19>>> age'19' 为了解决这个问题,可以使用函数 int() ,它让Python将输入视为数值: 12345>>> age = input(\"How old are you?\")How old are you?19>>> age = int(age)>>> age19 实例: 123456age = input(\"Please tell me your age:\")age = int(age)if age >= 18: print(\"You are old enough to go to the Internet bar!\")else: print(\"You are not old enough to go to Internet bar!\") 输出结果如下: 12Please tell me your age:17You are not old enough to go to Internet bar! - 6.1.2 求模运算符 处理数值信息时,求模运算符(%)是一个很有用的工具,它将两个数相除并返回余数: 12345678>>> 4 % 31>>> 5 % 32>>> 8 % 20>>> 7 % 31 - 6.1.3 在 Python 2.7 中获取输入 如果使用 Python 2.7,应该使用函数 raw_input() 来提示用户输入,这个函数与 Python 3 中的 input() 一样,也将输入解读为字符串;Python 2.7 也包含函数 input(),但它将用户输入解读为Python代码,并尝试运行它们 - 6.2 while 循环 for 循环用于针对集合中的每一个元素的一个代码块,而 while 循环不断地运行,直到指定的条件不满足为止 - 6.2.1 使用 while 循环 一个简单的 while 循环: 1234num = 1while num < 5: print(num) num += 1 输出结果如下: 12341234 - 6.2.2 让用户选择退出循环 123456prompt = \"\\nTell me something, and I will repeat it back to you:\"prompt += \"\\nEnter 'quit' to end the program.\"message = \" \"while message != 'quit': message = input(prompt) print(message) 运行程序: 123456789101112Tell me something, and I will repeat it back to you:Enter 'quit' to end the program.Hello everyone!Hello everyone!Tell me something, and I will repeat it back to you:Enter 'quit' to end the program.Hello again!Hello again!Tell me something, and I will repeat it back to you:Enter 'quit' to end the program.quitquit - 6.2.3 使用标志 在要求很多条件都满足才继续运行的程序中,可以定义一个变量,用于判断整个程序是否处于活动状态,这个变量称为标志 123456789prompt = \"\\nTell me something, and I will repeat it back to you:\"prompt += \"\\nEnter 'quit' to end the program.\"active = Truewhile active: message = input(prompt) if message == 'quit': active = False else: print(message) 运行结果与6.2.2一致 - 6.2.4 使用 break 退出循环 要立即退出 while 循环,不再运行循环中余下的代码,也不管条件测试的结果如何,可使用 break 语句,break 语句用于控制程序流程,可使用它来控制哪些代码将执行,哪些代码不执行 123456789prompt = \"\\nPlease enter the name of a city you have visited:\"prompt += \"\\nEnter 'quit' when you are finished.\"active = Truewhile active: city = input(prompt) if city == 'quit': break else: print(\"I'd love to go to \" + city.title() + \"!\") 运行程序: 1234567891011Please enter the name of a city you have visited:Enter 'quit' when you are finished.ShanghaiI'd love to go to Shanghai!Please enter the name of a city you have visited:Enter 'quit' when you are finished.BeijingI'd love to go to Beijing!Please enter the name of a city you have visited:Enter 'quit' when you are finished.quit 在任何Python循环中都可以使用break语句,例如,可以使用break语句来退出遍历列表或字典 - 6.2.5 在循环中使用 continue 要返回到循环开头,并根据条件测试结果决定是否继续执行循环,可使用 continue 语句,它不像 break 语句那样不再执行余下的代码并退出整个循环,例如,从1到10只打印其中奇数: 123456number =0while number < 10: number += 1 if number % 2 == 0: continue print(number) 输出结果如下:1234513579 - 6.3 使用 while 循环来处理列表和字典 for循环是一种遍历列表的有效方式,但在for循环中不应修改列表,否则将导致Python难以跟踪其中的元素,要在遍历列表的同时对其进行修改,可使用while循环 - 6.3.1 在列表之间移动元素 123456789unconfirmed_users = ['alice' , 'brian' , 'candace']confirmed_users = []while unconfirmed_users: current_user = unconfirmed_users.pop() print(\"Verifying user: \" + current_user.title()) confirmed_users.append(current_user)print(\"\\nThe following users have been confirmed:\")for confirmed_user in confirmed_users: print(confirmed_user.title()) 首先创建一个未验证用户列表,其中包含用户Alice、Brian和Candace,还创建了一个空列表,用于存储已验证的用户,程序中的 while 循环将不断地运行,直到列表 unconfirmed_users 变成空的。在这个循环中,函数pop() 以每次一个的方式从列表 unconfirmed_users 末尾删除未验证的用户。由于Candace位于列表 unconfirmed_users 的末尾,因此其名字将首先被删除、存储到变量 current_user 中并加入到列表 confirmed_users 中。接下来是Brian,然后是Alice 为模拟用户验证过程,我们打印一条验证消息并将用户加入到已验证用户列表中。未验证用户列表越来越短,而已验证用户列表越来越长。未验证用户列表为空后结束循环,再打印已验证用户列表: 12345678Verifying user: CandaceVerifying user: BrianVerifying user: AliceThe following users have been confirmed:CandaceBrianAlice - 6.3.2 删除包含特定值的所有列表元素 可以使用方法 remove() 来删除列表中特定的值,但如果要删除的值在列表中出现了多次,方法 remove() 就不管用了,如果要删除列表中所有包含特定值的元素则可以使用 while 循环: 12345names = ['alice' , 'candace' , 'alice' , 'brian' , 'alix' , 'candace' , 'heliy']print(names)while 'candace' in names: names.remove('candace')print(names) 输出结果如下: 12['alice', 'candace', 'alice', 'brian', 'alix', 'candace', 'heliy']['alice', 'alice', 'brian', 'alix', 'heliy'] 使用方法 remove() 做对比: 1234names = ['alice' , 'candace' , 'alice' , 'brian' , 'alix' , 'candace' , 'heliy']print(names)names.remove('candace')print(names) 输出结果如下: 12['alice', 'candace', 'alice', 'brian', 'alix', 'candace', 'heliy']['alice', 'alice', 'brian', 'alix', 'candace', 'heliy'] - 6.3.3 使用用户输入来填充字典 12345678910111213141516171819202122responses = {}#设置一个标志,指出调查是否继续polling_active = Truewhile polling_active: #提示输入被调查者的姓名和回答 name = input(\"\\nWhat's your name?\") response = input(\"What kind of fruit do you like?\") #将答卷储存在字典中 responses[name] = response #询问是否还有其他人要参与回答 repeat = input(\"Would you like to let another person respond?(Yes/No)\") if repeat == 'No': polling_active = False#调查结束,显示结果print(\"\\n------ Poll Results ------\")for name , response in responses.items(): print(name + \" like \" + response + \".\") 运行程序: 1234567891011What's your name?TRHXWhat kind of fruit do you like?appleWould you like to let another person respond?(Yes/No)YesWhat's your name?TRHXCCWhat kind of fruit do you like?bananaWould you like to let another person respond?(Yes/No)No------ Poll Results ------TRHX like apple.TRHXCC like banana.","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"基础学习","slug":"Python3-学习笔记/基础学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/基础学习/"}],"tags":[{"name":"input()函数","slug":"input-函数","permalink":"https://www.itrhx.com/tags/input-函数/"},{"name":"while循环","slug":"while循环","permalink":"https://www.itrhx.com/tags/while循环/"}]},{"title":"Python3 基础学习笔记 C05","slug":"A11-Python3-basic-C05","date":"2018-10-27T11:10:10.825Z","updated":"2019-09-24T12:45:35.909Z","comments":true,"path":"2018/10/27/A11-Python3-basic-C05/","link":"","permalink":"https://www.itrhx.com/2018/10/27/A11-Python3-basic-C05/","excerpt":"Python3 基础学习笔记第五章 —— 【字典】","text":"Python3 基础学习笔记第五章 —— 【字典】 - 5.1 一个简单的字典 123fruits = {'apple' : 'red' , 'number' : 5}print(fruits['apple'])print(fruits['number']) 输出结果如下: 12red5 在Python中,字典是一系列键-值对。每个键都与一个值相关联,你可以使用键来访问与之相关联的值。与键相关联的值可以是数字、字符串、列表乃至字典。事实上,可以将任何Python对象用作字典中的值。键-值对是两个相关联的值。在指定键时,Python将返回与之相关联的值。键和值之间用冒号分隔,而键-值对之间用逗号分隔。在字典中,想储存多少个键-值对都可以 - 5.1.1 访问字典中的值 要获取与键相关联的值,可依次指定字典名和放在方括号内的键: 123fruits = {'apple' : 'red' , 'number' : 5}number_fruits = fruits['number']print(\"The number of apple is \" + str(number_fruits) + \"!\") 输出结果如下: 1The number of apple is 5! - 5.1.2 添加键-值对 字典是一种动态结构,可随时在其中添加键-值对。要添加键-值对,可依次指定字典名、用方括号括起来的键和相关联的值 12345fruits = {'apple' : 'red' , 'number1' : 5}print(fruits)fruits['banana'] = 'yellow'fruits['number2'] = 13print(fruits) 输出结果如下: 12{'apple': 'red', 'number1': 5}{'apple': 'red', 'number1': 5, 'banana': 'yellow', 'number2': 13} 注意:键-值对的排列顺序与添加顺序不同。Python不关心键-值对的添加顺序,而只关心键和值之间的关联关系 有时候为了方便也可以先使用一对空的花括号定义一个字典,再分行添加各个键-值对: 1234fruits = {}fruits['banana'] = 'yellow'fruits['number2'] = 13print(fruits) 输出结果如下: 1{'banana': 'yellow', 'number2': 13} - 5.1.3 修改字典中的值 要修改字典中的值,可依次指定字典名、用方括号括起来的键以及与该键相关联的新值 1234fruits = {'color' : 'red'}print(\"The color of the fruits is \" + fruits['color'] + \"!\")fruits['color'] = 'yellow'print(\"The color of the fruits is \" + fruits['color'] + \" now!\") 输出结果如下: 12The color of the fruits is red!The color of the fruits is yellow now! 进阶:对一个能够以不同速度移动的外星人的位置进行跟踪,为此,我们将储存该外星人的当前速度,并据此确定该外星人将向右移动多远: 1234567891011121314alien = {'x_position': 0, 'y_position': 25, 'speed': 'medium'}print(\"Original x-position: \" + str(alien['x_position']))#向右移动外星人,据外星人当前速度决定将其移动多远if alien['speed'] == 'slow': x_increment = 1elif alien['speed'] == 'medium': x_increment = 2else: x_increment = 3#新位置等于老位置加上增量alien['x_position'] = alien['x_position'] + x_incrementprint(\"New x_position: \" + str(alien['x_position'])) 输出结果如下: 12Original x-position: 0New x_position: 2 - 5.1.4 删除键-值对 对于字典中不再需要的信息,可使用del语句将相应的键-值对彻底删除。使用del语句时,必须指定字典名和要删除的键 1234fruits = {'apple' : 'red' , 'number' : 5}print(fruits)del fruits['number']print(fruits) 输出结果如下: 12{'apple': 'red', 'number': 5}{'apple': 'red'} - 5.1.5 由类似对象组成的字典 字典储存的可以是一个对象的多种信息,也可以储存众多对象的同一种信息,例如要调查很多人最喜欢的编程语言: 1234567favorite_languages = { 'jen' : 'python' , 'sarah' : 'c' , 'edward' : 'ruby' , 'phil' : 'java' , }print(\"Sarah's favorite languages is \" + favorite_languages['sarah'].title() + \"!\") 输出结果如下: 1Sarah's favorite languages is C! - 5.2 遍历字典 - 5.2.1 方法 items() 遍历所有的键-值对 使用for循环来遍历字典:12345678name = { 'username' : 'efermi' , 'first' : 'enrico' , 'last' : 'fermi' , }for key , value in name.items(): print(\"\\nKey: \" + key) print(\"Value: \" + value) 输出结果如下:123456789Key: usernameValue: efermiKey: firstValue: enricoKey: lastValue: fermi for语句的第二部分包含字典和方法items(),它返回一个键-值对列表。接下来,for循环依次将每个键-值对储存到指定的两个变量中 12345678favorite_languages = { 'jen' : 'python' , 'sarah' : 'c' , 'edward' : 'ruby' , 'phil' : 'java' , }for name, language in favorite_languages.items(): print(name.title() + \"'s favorite language is \" + language.title() + \".\") 输出结果如下: 1234Jen's favorite language is Python.Sarah's favorite language is C.Edward's favorite language is Ruby.Phil's favorite language is Java. - 5.2.2 方法 keys() 遍历字典中所有的键 在不需要使用字典中的值时,方法key()很有用,下面来遍历字典favorite_languages,并将每个被调查者的名字都打印出来: 12345678favorite_languages = { 'jen' : 'python' , 'sarah' : 'c' , 'edward' : 'ruby' , 'phil' : 'java' , }for name in favorite_languages.keys(): print(name.title()) 输出结果如下: 1234JenSarahEdwardPhil 遍历字典时,会默认遍历所有的键,因此,如果将上述代码中的for name in favorite_languages.keys():替换为for name in favorite_languages:输出结果将不变进阶: 1234567891011favorite_languages = { 'jen' : 'python' , 'sarah' : 'c' , 'edward' : 'ruby' , 'phil' : 'java' , }friends = ['phil', 'sarah']for name in favorite_languages.keys(): print(name.title()) if name in friends: print(\"Hi \" + name + \", I see your favorite languages is \" + favorite_languages[name].title() + \"!\") 输出结果如下: 123456JenSarahHi sarah, I see your favorite languages is C!EdwardPhilHi phil, I see your favorite languages is Java! - 5.2.3 函数 sorted() 按顺序遍历字典中的所有键 字典总是明确地记录键和值之间的关联关系,但获取字典的元素时,获取顺序是不可预测的,要以特定的顺序返回元素,一种办法是在for循环中对返回的键进行排序,为此,可以使用函数sorted()来获得按特定顺序排列的键列表的副本: 12345678favorite_languages = { 'jen' : 'python' , 'sarah' : 'c' , 'edward' : 'ruby' , 'phil' : 'java' , }for name in sorted(favorite_languages.keys()): print(name.title()) 输出结果如下: 1234EdwardJenPhilSarah - 5.2.4 方法 values() 遍历字典中的所有值 12345678favorite_languages = { 'jen' : 'python' , 'sarah' : 'c' , 'edward' : 'ruby' , 'phil' : 'java' , }for languages in favorite_languages.values(): print(languages.title()) 输出结果如下: 1234PythonCRubyJava 这种做法提取字典中所有的值,而没有考虑是否重复,为剔除重复项,可使用集合(set),集合类似于列表,但每个元素都必须是独一无二的: 12345678favorite_languages = { 'jen' : 'python' , 'sarah' : 'c' , 'edward' : 'ruby' , 'phil' : 'python' , }for languages in set(favorite_languages.values()): print(languages.title()) 输出结果如下: 123CPythonRuby - 5.3 嵌套 有时候,需要将一系列字典储存在列表中,或将列表作为值储存在字典中,这称为嵌套。可以在列表中嵌套字典、在字典中嵌套列表甚至在字典中嵌套字典 - 5.3.1 字典列表 下面代码创建三个字典,每个字典都表示一个个学生,将这三个字典都放到一个名为students的列表当中,遍历列表将每个学生都打印出来: 123456student_0 = {'name' : 'anily' , 'class' : 2}student_1 = {'name' : 'nikey' , 'class' : 5}student_2 = {'name' : 'heyk' , 'class' : 3}students = [student_0 , student_1 , student_2]for student in students: print(student) 输出结果如下: 123{'name': 'anily', 'class': 2}{'name': 'nikey', 'class': 5}{'name': 'heyk', 'class': 3} 进阶:使用 range() 自动生成三十个外星人: 123456789101112131415#创建一个用于存储外星人的空列表aliens = []#创建三十个绿色的外星人for alien_number in range(30): new_alien = {'color' : 'green' , 'points' : 5 , 'speed' : 'slow'} aliens.append(new_alien)#显示前五个外星人for alien in aliens[:5]: print(alien)print(\"......\")#显示创建了多少外星人print(\"Total number of aliens: \" + str(len(aliens))) 输出结果如下: 1234567{'color': 'green', 'points': 5, 'speed': 'slow'}{'color': 'green', 'points': 5, 'speed': 'slow'}{'color': 'green', 'points': 5, 'speed': 'slow'}{'color': 'green', 'points': 5, 'speed': 'slow'}{'color': 'green', 'points': 5, 'speed': 'slow'}......Total number of aliens: 30 在上述例子中,虽然每个外星人都具有相同特征,但在Python看来,每个外星人都是独立的,我们可以独立地修改每个外星人: 12345678910111213aliens = []for alien_number in range(30): new_alien = {'color' : 'green' , 'points' : 5 , 'speed' : 'slow'} aliens.append(new_alien)for alien in aliens[0:3]: if alien['color'] == 'green': alien['color'] = 'yellow' alien['points'] = 10 alien['speed'] = 'medium'for alien in aliens[:5]: print(alien)print(\"......\")print(\"Total number of aliens: \" + str(len(aliens))) 输出结果如下: 1234567{'color': 'yellow', 'points': 10, 'speed': 'medium'}{'color': 'yellow', 'points': 10, 'speed': 'medium'}{'color': 'yellow', 'points': 10, 'speed': 'medium'}{'color': 'green', 'points': 5, 'speed': 'slow'}{'color': 'green', 'points': 5, 'speed': 'slow'}......Total number of aliens: 30 - 5.3.2 在字典中存储列表 有时候需要将列表储存在字典中,而不是将字典储存在列表中:例一: 12345678910#储存所点比萨的信息pizza = { 'crust' : 'thick' , 'toppings' : ['mushrooms' , 'extra chees'] , }#概述所点的比萨print(\"You ordered a \" + pizza['crust'] + \"-crust pizza\" + \"with the following toppings :\" )for topping in pizza['toppings']: print(\"\\t\" + topping) 输出结果如下: 123You ordered a thick-crust pizzawith the following toppings : mushrooms extra chees 例二: 12345678910favorite_languages = { 'jen' : ['python' , 'ruby'] , 'sarah' : ['c'] , 'edward' : ['go' , 'ruby'] , 'phil' : ['python' , 'java'] , }for name , languages in favorite_languages.items(): print(\"\\n\" + name.title() + \"'s favorite languages are:\") for language in languages: print(\"\\t\" + language.title()) 输出结果如下: 123456789101112131415Jen's favorite languages are: Python RubySarah's favorite languages are: CEdward's favorite languages are: Go RubyPhil's favorite languages are: Python Java - 5.3.3 在字典中存储字典 123456789101112131415161718users = { 'aeinstein' : { 'first' : 'albert' , 'last' : 'einstein' , 'location' : 'princeton' , } , 'mcurie' : { 'first' : 'marie' , 'last' : 'curie' , 'location' : 'paris' , } , }for username , user_info in users.items(): print(\"\\nUsername : \" + username) full_name = user_info['first'] + \" \" + user_info['last'] location = user_info['location'] print(\"\\tFull name : \" + full_name.title()) print(\"\\tlocation : \" + location .title()) 输出结果如下: 12345678Username : aeinstein Full name : Albert Einstein location : PrincetonUsername : mcurie Full name : Marie Curie location : Paris","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"基础学习","slug":"Python3-学习笔记/基础学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/基础学习/"}],"tags":[{"name":"字典","slug":"字典","permalink":"https://www.itrhx.com/tags/字典/"}]},{"title":"Python3 基础学习笔记 C04","slug":"A10-Python3-basic-C04","date":"2018-10-23T16:57:39.886Z","updated":"2019-09-24T12:45:32.554Z","comments":true,"path":"2018/10/24/A10-Python3-basic-C04/","link":"","permalink":"https://www.itrhx.com/2018/10/24/A10-Python3-basic-C04/","excerpt":"Python3 基础学习笔记第四章 —— 【if语句】","text":"Python3 基础学习笔记第四章 —— 【if语句】 - 4.1 一个简单的数列 给定一个汽车列表,将其中每一辆汽车的名称打印出来,要求打印 ‘bmw’ 时所有字母都要大写,其余名称只需要首字母大写: 123456cars = ['audi' , 'bmw' , 'subaru' , 'toyota']for car in cars: if car == 'bmw': print(car.upper())else: print(car.title()) 输出结果如下: 1234AudiBMWSubaruToyota - 4.1.1 检查特定值是否包含在列表当中 要判断特定的值是否已包含在列表当中,可使用关键字 in 1234user_names = ['andia' , 'david' , 'liwa']user = 'andia'if user in user_names: print(user.title() + \"is in user_name.\") 输出结果如下: 1Andiais in user_name. 要判断特定的值是否不包含在列表当中,可使用关键字 not in 1234user_names = ['andia' , 'david' , 'liwa']user = 'kivle'if user not in user_names: print(user.title() + \"is not in user_name.\") 输出结果如下: 1Kivleis not in user_name. - 4.2 if-else 语句 1234567age = input(\"请输入你的年龄查看是否可以去网吧:\")if int(age) >= 18: print(\"You are old enough to go to the net bar!\") print(\"You should go to net bar less,study more!\")else: print(\"You are too young to go to the net bar!\") print(\"Wait until you are 18 to go to the net bar!\") 分别输入19和15,输出结果如下: 123请输入你的年龄查看是否可以去网吧:19You are old enough to go to the net bar!You should go to net bar less,study more! 123请输入你的年龄查看是否可以去网吧:15You are too young to go to the net bar!Wait until you are 18 to go to the net bar! - 4.3 if-elif-else 结构 12345678age = 12if age < 4: price = 0elif age < 18: price = 5else: price = 10print(\"Your admission cost is $\" + str(price) + \".\") 输出结果如下: 1Your admission cost is $5. - 4.3.1 使用多个 elif 代码块 12345678910age = 20if age < 4: price = 0elif age < 18: price = 5elif age < 65: price = 15else: price = 10print(\"Your admission cost is $\" + str(price) + \".\") 输出结果如下: 1Your admission cost is $15. - 4.3.2 省略 else 代码块 Python并不要求 if-elif 结构后面必须有 else 代码块: 12345678910age = 20if age < 4: price = 0elif age < 18: price = 5elif age < 65: price = 15elif age >= 65: price = 10print(\"Your admission cost is $\" + str(price) + \".\") 输出结果仍与3.3.1一样 - 4.4 测试多个条件 if-elif-else结构功能强大,但仅适用于只有一个条件满足的情况:遇到通过了的测试后,Python就会跳过余下的测试: 12345678 names = ['Zhangshan' , 'Wanger']if 'Zhangshan' in names: print(\"Zhangshan is here!\")if 'Wanger' in names: print(\"Wanger is here!\")if 'Xiaoming' in names: print(\"Xiaoming is here!\")print(\"All the students are here!\") 输出结果如下: 123Zhangshan is here!Wanger is here!All the students are here! 相同的程序,如果使用 if-elif-else 结构,代码将不能正确运行: 12345678names = ['Zhangshan' , 'Wanger']if 'Zhangshan' in names: print(\"Zhangshan is here!\")elif 'Wanger' in names: print(\"Wanger is here!\")elif 'Xiaoming' in names: print(\"Xiaoming is here!\")print(\"All the students are here!\") 输出结果如下:12Zhangshan is here!All the students are here! 总之,如果我们只想执行一个代码块,就使用 if-elif-else 结构;如果要运行多个代码块,就必须使用一系列独立的 if 语句! - 4.5 使用 if 语句处理列表 - 4.5.1 检查特殊元素对3.4例子改版,加入姓名 ‘Xiaoming’,当检索到Xiaoming时告诉他,他妈妈叫他回家吃饭1234567names = ['Zhangshan' , 'Wanger' , 'Xiaoming']for name in names: if name == 'Xiaoming': print(\"Xiaoming,Your mother told you to go home for dinner!\") else: print(name +\"is here!\")print(\"All the students are here!\") 输出结果如下: 1234Zhangshanis here!Wangeris here!Xiaoming,Your mother told you to go home for dinner!All the students are here! - 4.5.2 确定列表不是空的 在检索姓名前检查姓名是否为空,不为空则打印出所有姓名,为空则提示没有姓名: 1234567names = []if names: for name in names: print(name +\" is here!\") print(\"All the students are here!\")else: print(\"There is no students!\") 输出结果如下: 1There is no students! 在if语句中将列表名用在条件表达式中时,Python将在列表至少包含一个元素时返回Ture,并在列表为空时返回False - 4.5.3 使用多个列表 两个列表names_1和names_2,要求输出既在names_2中又在names_1中的元素: 123456names_1 = ['Zhangshan' , 'Liyang' , 'Wanger' , 'Tangyang' , 'Xiaoming']names_2 = ['Liyang' , 'Zhangwei' , 'Tangyang']for names in names_2: if names in names_1: print(names +\" is here!\")print(\"All the students are here!\") 输出结果如下: 123Liyang is here!Tangyang is here!All the students are here!","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"基础学习","slug":"Python3-学习笔记/基础学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/基础学习/"}],"tags":[{"name":"if语句","slug":"if语句","permalink":"https://www.itrhx.com/tags/if语句/"}]},{"title":"Python3 基础学习笔记 C03","slug":"A09-Python3-basic-C03","date":"2018-10-11T15:03:18.487Z","updated":"2019-09-24T12:45:28.649Z","comments":true,"path":"2018/10/11/A09-Python3-basic-C03/","link":"","permalink":"https://www.itrhx.com/2018/10/11/A09-Python3-basic-C03/","excerpt":"Python3 基础学习笔记第三章 —— 【操作列表】","text":"Python3 基础学习笔记第三章 —— 【操作列表】 - 3.1遍历整个列表 使用 for 循环来遍历整个列表: 123names = ['alice' , 'david' , 'liwei']for name in names:print(name) 输出结果如下: 123alicedavidliwei for循环让Python从列表names中取出一个名字,并将其储存在变量name中,最后 让Python打印前面储存到变量name中的名字,对于列表中的每个名字,Python都将 重复执行后两行代码,将列表names中的每个名字都打印出来 - 3.1.1在for循环中执行更多的操作 在for循环中,可对每个元素执行任何操作,下面对前面的示例进行扩展: 例一:123names = ['alice' , 'david' , 'liwei']for name in names: print(name.title() + \", that was a good man!\") 输出结果如下: 123Alice, that was a good man!David, that was a good man!Liwei, that was a good man! 例二: 12345names = ['alice' , 'david' , 'liwei']for name in names: print(name.title() + \", that was a good man!\") print(\"I can't wait to see you again,\" + name.title() + \".\\n\")print(\"Nice to meet you!\") 输出结果如下: 12345678910Alice, that was a good man!I can't wait to see you again,Alice.David, that was a good man!I can't wait to see you again,David.Liwei, that was a good man!I can't wait to see you again,Liwei.Nice to meet you! - 3.2 range()函数 Python使用range()函数能够轻松地生成一系列的数字 Python3 range() 函数返回的是一个可迭代对象(类型是对象),而不是列表类型, 所以打印的时候不会打印列表; Python3 list() 函数是对象迭代器,可以把range()返回的可迭代对象转为一个列表,返回的变量类型为列表; Python2 range() 函数返回的是列表 例一:12for i in range(1,5): print(i) 输出结果如下: 12341234 例二:12for i in range(5): print(i) 输出结果如下:1234501234 例三:123456789101112>>> list(range(5))[0, 1, 2, 3, 4]>>> list(range(0))[]>>>list(range(0, 30, 5))[0, 5, 10, 15, 20, 25]>>> list(range(0, 10, 2))[0, 2, 4, 6, 8]>>> list(range(0, -10, -1))[0, -1, -2, -3, -4, -5, -6, -7, -8, -9]>>> list(range(1, 0))[] 例四: 12345squares = []for value in range(1,11): square = value ** 2 squares.append(square)print(squares) 输出结果如下: 1[1, 4, 9, 16, 25, 36, 49, 64, 81, 100] - 3.2.1 对数字列表执行简单的统计计算 1234567>>> digits = [1, 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 0]>>> min(digits)0>>>max(digits)9>>>sum(digits)45 - 3.2.2 列表解析 列表解析能够让比如3.2中的例四更加简化,只需要一行代码就能生成这样的列表,列表解析将for循环和创建新元素的代码合并成一行,并自动附加新元素: 12squares = [value ** 2 for value in range(1,11)]print(squares) 在这个示例中,for循环为for value in range(1,11),它将值1~10提供给表达式value ** 2输出结果如下: 1[1, 4, 9, 16, 25, 36, 49, 64, 81, 100] - 3.3 使用列表的一部分 处理列表的部分元素——Python称之为切片 - 3.3.1 切片 1234567891011list = ['a','b','c','d','e','f']print(list[:]) #省略全部,代表截取全部内容,可以用来将一个列表拷给另一个列表print(list[:3]) #省略起始位置的索引,默认起始位置从头开始,结束位置索引为2print(list[3:]) #省略结束位置的索引,默认结束位置为最后一个,开始位置索引为3print(list[1:4]) #开始位置索引为1,结束位置索引为3,顾头不顾尾print(list[4:1]) #从左到右索引,因此为空值print(list[-1:-3]) #从左到右索引,因此为空值print(list[-3:-1]) #开始位置索引为倒数第三个,结束位置索引为倒数第二个print(list[1:5:2]) #开始位置索引为1,结束位置索引为4,间隔2print(list[5:1:-1]) #反向取值,开始位置索引为5,结束位置索引为2print(list[::-1]) #反向取值,反向输出列表 - 3.3.2 遍历列表 1234players = ['charles' , 'martina' , 'michael' , 'florence' , 'eli']print(\"Here are the first three players on my team:\")for player in players[:3]: print(player.title()) 输出结果如下: 1234Here are the first three players on my team:CharlesMartinaMichael - 3.3.3 复制列表 要复制列表,可以创建一个包含整个列表的切片,方法是同时省略起始索引和终止索引([:]),这让Python创建一个始于第一个元素,终止于最后一个元素的切片,即复制整个列表: 123456my_foods = ['pizza' , 'falafel' , 'carrot cake']friend_foods = my_foods[:]print(\"My favorite foods are:\")print(my_foods)print(\"\\nMy friend's favorite foods are:\")print(friend_foods) 输出结果如下: 12345My favorite foods are:['pizza', 'falafel', 'carrot cake']My friend's favorite foods are:['pizza', 'falafel', 'carrot cake'] 为核实我们的确有两个列表,下面在每个列表中都添加一种食品,并核实每个列表都记录了相应人员喜欢的食品:12345678910my_foods = ['pizza' , 'falafel' , 'carrot cake']friend_foods = my_foods[:]my_foods.append('cannoli')friend_foods.append('ice cream')print(\"My favorite foods are:\")print(my_foods)print(\"\\nMy friend's favorite foods are:\")print(friend_foods) 输出结果如下: 12345My favorite foods are:['pizza', 'falafel', 'carrot cake', 'cannoli']My friend's favorite foods are:['pizza', 'falafel', 'carrot cake', 'ice cream'] 输出结果表明,’cannoli’包含在我喜欢的食品列表中,而’ice cream’没有;’ice cream’包含在我朋友喜欢的食品中,而’cannoli’没有,假如我们只是简单的将my_foods赋给friend_foods,就不能得到两个列表。下面是错误示例: 12345678910my_foods = ['pizza' , 'falafel' , 'carrot cake']friend_foods = my_foods #错误写法my_foods.append('cannoli')friend_foods.append('ice cream')print(\"My favorite foods are:\")print(my_foods)print(\"\\nMy friend's favorite foods are:\")print(friend_foods) 错误示例输出结果如下: 12345My favorite foods are:['pizza', 'falafel', 'carrot cake', 'cannoli', 'ice cream']My friend's favorite foods are:['pizza', 'falafel', 'carrot cake', 'cannoli', 'ice cream'] - 3.4 元组 Python将不能修改的值称为不可变的,而不可变的列表被称为元组 - 3.4.1 定义元组 元组看起来就像是列表,但元组使用圆括号而不是方括号来标识,定义元组后,就可以使用索引来访问其元素,就像访问列表元素一样: 123dimensions = (200,50)print(dimensions[0])print(dimensions[1]) 输出结果如下: 1220050 如果尝试修改元组中元素的值,将会导致Python返回类型错误消息,由于试图修改元组的操作是被禁止的,因此Python指出不能给元组的元素赋值: 12dimensions = (200,50)dimensions[0] = 300 将会报错: 1234Traceback (most recent call last): File \"dimensions.py\", line 2, in <module> dimensions[0] = 300TypeError: 'tuple' object does not support item assignment - 3.4.2 遍历元组中所有的值 像列表一样,元组也可以使用for循环来遍历元组中的所有值: 例一:123dimensions = (200,100,50,6)for dimension in dimensions: print(dimension) 输出结果如下: 1234200100506 例二: 123dimensions = (200,100,50,6)for dimension in dimensions[:3]: print(dimension) 输出结果如下: 12320010050 - 3.4.3 修改元组变量 虽然不能修改元组元素,但是可以给储存元组的变量赋值: 123456789dimensions = (200,50)print(\"Original dimensions:\")for dimension in dimensions: print(dimension) dimensions = (400,100)print(\"\\nModified dimensions:\")for dimension in dimensions: print(dimension) 输出结果如下: 1234567Original dimensions:20050Modified dimensions:400100 我们首先定义了一个元组,并将其储存的尺寸打印了出来;然后将一个新元组储存到变量dimensions中,打印新的尺寸;相比于列表,元组是更简单的数据结构。如果需要储存的一组值在程序的整个生命周期内都不变,可使用元组","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"基础学习","slug":"Python3-学习笔记/基础学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/基础学习/"}],"tags":[{"name":"操作列表","slug":"操作列表","permalink":"https://www.itrhx.com/tags/操作列表/"}]},{"title":"Python3 基础学习笔记 C02","slug":"A08-Python3-basic-C02","date":"2018-09-15T17:59:48.972Z","updated":"2019-09-24T12:45:24.985Z","comments":true,"path":"2018/09/16/A08-Python3-basic-C02/","link":"","permalink":"https://www.itrhx.com/2018/09/16/A08-Python3-basic-C02/","excerpt":"Python3 基础学习笔记第二章 —— 【列表】","text":"Python3 基础学习笔记第二章 —— 【列表】 - 2.1列表是什么 列表由一系列按特定顺序的元素组成,在 Python 中用方括号( [ ] )来表示列表,并用逗号来分隔其中的元素,例: 12345list1 = ['a','b','c','d','e','f']list2 = ['abc', 'xyz', 2018, 2020]list3 = [1, 2, 3, 4, 5 ,6]list4 = [\"a\", \"b\", \"c\", \"d\"]print(list1, list2, list3 ,list4) 输出结果如下: 1['a', 'b', 'c', 'd', 'e', 'f'] ['abc', 'xyz', 2018, 2020] [1, 2, 3, 4, 5, 6] ['a', 'b', 'c', 'd'] - 2.1.1访问列表元素 列表是有序集合,因此要访问列表的元素,只需要将该元素的位置或索引告诉Python即可,注意:在Python中的第一个列表元素的索引为0,而不是1 12345list = ['a','b','c','d','e','f']print(list[0])print(list[3])print(list[-1]) #Python为访问最后一个列表元素提供了一种特殊语法,通过将索引指定为-1,可以让Python返回最后一个列表元素print(list[-3]) 输出结果如下: 1234adfd - 2.1.2列表切片 1234567891011list = ['a','b','c','d','e','f']print(list[:]) #省略全部,代表截取全部内容,可以用来将一个列表拷给另一个列表print(list[:3]) #省略起始位置的索引,默认起始位置从头开始,结束位置索引为2print(list[3:]) #省略结束位置的索引,默认结束位置为最后一个,开始位置索引为3print(list[1:4]) #开始位置索引为1,结束位置索引为3,顾头不顾尾print(list[4:1]) #从左到右索引,因此为空值print(list[-1:-3]) #从左到右索引,因此为空值print(list[-3:-1]) #开始位置索引为倒数第三个,结束位置索引为倒数第二个print(list[1:5:2]) #开始位置索引为1,结束位置索引为4,间隔2print(list[5:1:-1]) #反向取值,开始位置索引为5,结束位置索引为2print(list[::-1]) #反向取值,反向输出列表 输出结果如下: 12345678910['a', 'b', 'c', 'd', 'e', 'f']['a', 'b', 'c']['d', 'e', 'f']['b', 'c', 'd'][][]['d', 'e']['b', 'd']['f', 'e', 'd', 'c']['f', 'e', 'd', 'c', 'b', 'a'] - 2.1.3使用列表中的各个值 可像使用其他变量一样使用列表中的各个值,例如,我们可以使用拼接根据列表中的值来创建消息: 123list = ['python', 'c', 'c++', 'java', 'php']message = \"My favorite language is \" + list[0].title() + \"!\"print(message) 输出结果如下: 1My favorite language is Python! - 2.1.4修改元素 修改列表元素的语法与访问列表元素的语法类似,要修改列表元素,可指定列表名和要修改的元素的索引,再次指定该元素的新值: 1234names = ['zhangsan', 'lishi', 'wanger', 'liming', 'xiaowang']print(names)names[1] = 'lifang'print(names) 输出结果如下: 12['zhangsan', 'lishi', 'wanger', 'liming', 'xiaowang']['zhangsan', 'lifang', 'wanger', 'liming', 'xiaowang'] - 2.1.5添加元素 - 使用方法 append() 在列表末尾添加元素 1234list = ['a', 'b', 'c', 'd', 'e', 'f']print(list)list.append('g')print(list)输出结果如下:12['a', 'b', 'c', 'd', 'e', 'f']['a', 'b', 'c', 'd', 'e', 'f', 'g'] - 使用方法 insert() 在列表指定位置添加元素 1234list = ['a', 'b', 'c', 'd', 'e', 'f']print(list)list.insert(2,\"h\") #其中括号里的数字表示要插入的位置,此后面的元素将右移一个位置print(list) 输出结果如下: 12['a', 'b', 'c', 'd', 'e', 'f']['a', 'b', 'h', 'c', 'd', 'e', 'f', 'g'] - 2.1.6删除元素 - 使用 del 语句删除元素 1234list = ['a', 'b', 'c', 'd', 'e', 'f']print(list)del list[3]print(list) 输出结果如下: 12list = ['a', 'b', 'c', 'd', 'e', 'f']list = ['a', 'b', 'c', 'e', 'f'] - 使用方法 pop() 删除最后一个元素方法 pop() 可以删除列表末尾的元素,并让你能够接着使用它。术语弹出(pop)源自这样的类比:列表就像是一个栈,而删除列表末尾的元素就相当于弹出栈顶元素:12345list = ['a', 'b', 'c', 'd', 'e', 'f']print(list)new_list = list.pop()print(list)print(new_list)输出结果如下:123['a', 'b', 'c', 'd', 'e', 'f']['a', 'b', 'c', 'd', 'e']f - 使用方法 pop() 删除任意位置元素可以使用 pop() 来删除列表中任何位置的元素,只需要在括号中指定要删除的元素的索引即可:12345list = ['a', 'b', 'c', 'd', 'e', 'f']print(list)new_list = list.pop(1)print(list)print(new_list)输出结果如下:123['a', 'b', 'c', 'd', 'e', 'f']['a', 'c', 'd', 'e', 'f']b - 使用方法 remove() 删除未知位置元素当我们不知道元素的位置,只知道元素的值的时候,就可以使用方法 remove()1234list = ['a', 'b', 'c', 'd', 'e', 'f']print(list)list.remove('d')print(list)输出结果如下:12['a', 'b', 'c', 'd', 'e', 'f']['a', 'b', 'c', 'e', 'f'] # - 2.1.7使用方法 index() 查找指定元素位置 12list = [\"a\", \"b\", \"c\", \"d\", \"e\", \"a\"]print(list.index('c')) 输出结果如下: 12 - 2.1.8使用方法 count() 统计指定元素数量 12list = [\"a\", \"b\", \"c\", \"d\", \"e\", \"a\"]print(list.count('a')) 输出结果如下: 12 - 2.1.9清空列表 123list = [\"a\", \"b\", \"c\", \"d\", \"e\", \"a\"]list.clear()print(list) 输出结果如下: 1[] - 2.2组织列表 在创建的列表中,元素的排列顺序常常是无法预测的,因为我们并非总能控制用户提供数据的顺序。有时候,我们希望保留列表元素最初的排列顺序,而有时候又需要调整排列顺序。Python提供了很多组织列表的方式,可根据具体情况选用 - 2.2.1使用方法 sort() 对列表进行永久排序 使用方法 sort() 可以对列表按照特殊符号,数字,大写字母,小写字母顺序进行永久排序: 123cars = ['bmw', 'audi', 'toyota', 'subaru']cars.sort()print(cars) 输出结果如下: 1['audi', 'bmw', 'subaru', 'toyota'] 还可以按与字母顺序相反的顺序排列列表元素,只需要向 sort() 方法传递参数 reverse = True 就可以了: 123cars = ['bmw', 'audi', 'toyota', 'subaru']cars.sort(reverse = True)print(cars) 输出结果如下: 1['toyota', 'subaru', 'bmw', 'audi'] - 2.2.2使用函数 sorted() 对列表进行临时排序 要保留列表元素原来的排列顺序,同时以特定的顺序呈现它们,可使用函数sorted()。函数sorted()让你能够按特定顺序显示列表元素,同时不影响它们在列表中的原始排列顺序: 123456789cars = ['bmw', 'audi', 'toyota', 'subaru']print(\"Here is the original list:\")print(cars)print(\"\\nHere is the sorted list:\")print(sorted(cars))print(\"\\nHere is the sorted reverse list:\")print(sorted(cars, reverse=True))print(\"\\nHere is the original list again:\")print(cars) 输出结果如下: 1234567891011Here is the original list:['bmw', 'audi', 'toyota', 'subaru']Here is the sorted list:['audi', 'bmw', 'subaru', 'toyota']Here is the sorted reverse list:['toyota', 'subaru', 'bmw', 'audi']Here is the original list again:['bmw', 'audi', 'toyota', 'subaru'] - 2.2.3使用方法 reverse() 对列表进行反向排序 要反转列表元素的排列顺序,可使用方法 reverse() 123cars = ['bmw', 'audi', 'toyota', 'subaru']cars.reverse()print(cars) 输出结果如下: 1['subaru', 'toyota', 'audi', 'bmw'] - 2.2.4确定列表的长度 使用函数 len() 可以快速获悉列表的长度: 123>>>cars = ['bmw', 'audi', 'toyota', 'subaru']>>>len(cars)4 - 2.2.5合并列表 - 使用方法 extend() 合并列表 12345list1 = [1, 2, 3, 4]list2 = ['a', 'b', 'c', 'd']list1.extend(list2) #将列表list2添加到list1当中去print(list1)print(list2) 输出结果如下: 12[1, 2, 3, 4, 'a', 'b', 'c', 'd']['a', 'b', 'c', 'd'] - 使用 “+” 号合并列表 1234list1 = [1, 2, 3, 4]list2 = ['a', 'b', 'c', 'd']print(list1 + list2)print(list2 + list1) 输出结果如下: 12[1, 2, 3, 4, 'a', 'b', 'c', 'd']['a', 'b', 'c', 'd', 1, 2, 3, 4] - 使用切片合并列表 1234567891011121314list1 = [1, 2, 3, 4]list2 = ['a', 'b', 'c', 'd']list1[len(list1) : len(list1)] = list2 #len(list1)代表要将list2插入list1中的位置print(list1)list1 = [1, 2, 3, 4]list2 = ['a', 'b', 'c', 'd']list1[0 :0] = list2print(list1)list1 = [1, 2, 3, 4]list2 = ['a', 'b', 'c', 'd']list1[1:1] = list2print(list1) 输出结果如下: 123[1, 2, 3, 4, 'a', 'b', 'c', 'd']['a', 'b', 'c', 'd', 1, 2, 3, 4][1, 'a', 'b', 'c', 'd', 2, 3, 4]","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"基础学习","slug":"Python3-学习笔记/基础学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/基础学习/"}],"tags":[{"name":"列表","slug":"列表","permalink":"https://www.itrhx.com/tags/列表/"}]},{"title":"Python3 基础学习笔记 C01","slug":"A07-Python3-basic-C01","date":"2018-09-13T12:47:09.608Z","updated":"2019-12-29T07:27:42.544Z","comments":true,"path":"2018/09/13/A07-Python3-basic-C01/","link":"","permalink":"https://www.itrhx.com/2018/09/13/A07-Python3-basic-C01/","excerpt":"Python3 基础学习笔记第一章 —— 【变量和简单数据类型】","text":"Python3 基础学习笔记第一章 —— 【变量和简单数据类型】 - 1.1变量的命名和使用 变量名只能包含字母、数字和下划线。变量名可以字母或者下划线打头,但不能以数字开头,例如,可以将变量命名为message_1,但不能将其命名为1_message 变量名不能包含空格,但可使用下划线来分割其中的单词,例如,变量名greeting_message可行,但变量名greeting message会引发错误 不要将Python关键字和函数名用作变量名,即不要使用Python保留用于特殊用途的单词,如print 变量名应既简短又具有描述性,例如,name比n好,student_name比s_n好,name_length比length_of_persons_name好 慎用小写字母l和大写字母O,因为它们可能被人看错成数字1和0 - 1.2字符串 字符串就是一系列字符,在Python中,用引号括起来的都是字符串,其中的引号可以是单引号也可以双引号: 12\"This is a string.\"'This is also a string.' 这种灵活性让我们能够在字符串中包含引号和撇号: 123'I told my friend,\"Python is my favorite language!\"'\"The language 'Python' is named er Monty Python,not the snake.\"\"One of Python's strengths is i diverse and supportive community.\" - 1.2.1使用方法修改字符串的大小写三种处理方法如下:123title() #将字符串每个单词的首字母都改为大写upper() #将字符串的每个字母都改为大写lower() #将字符串的每个字母都改为小写 例如:1234message = \"I love you!\"print(name.title())print(name.upper())print(name.lower()) 输出结果如下:123I Love You!I LOVE YOU!i love you! - 1.2.2合并(拼接)字符串Python使用加号(+)来合并字符串,举例说明: 12345first_name = \"I\"second_name = \"love\"third_name = \"python\"full_name = first_name + \" \" + second_name + \" \" + third_timeprint(full_name.title() + \"!\") 输出结果如下: 1I Love Python! - 1.2.3使用制表符或换行符来添加空白添加横向制表符: 12>>>print(\"\\tPython\") Python 添加换行符: 12345>>>print(\"C\\nC++\\nPython\\nJavaScript\")CC++PythonJavaScript 附表:Python转义符 - 1.2.4删除空白在Python中可用 lstrip()、rstrip()、strip() 分别删除字符串开头、结尾、全部的空白,举例说明: 123456789>>>message = ' python '>>>message' python '>>>message.lstrip()'python '>>>message.rstrip()' python'>>>message.strip()'python' 如果要永久删除字符串中的空白,必须将删除操作的结果存回到变量中: 1234>>>message = ' python '>>>message = message.strip()>>>message'python' - 1.3数字在编程中,经常使用数字来记录游戏得分、表示可视化数据、储存Web应用信息等。Python根据数字的用法以不同的方式处理它们 - 1.3.1整数在Python中,可对整数执行加(+)减(-)乘(*)除(/)乘方(**)运算,同时也支持运算次序: 12345678910111213141516>>>3 + 25>>>3 - 21>>>3 * 26>>>3 \\ 21.5>>>3 ** 29>>>3 ** 327>>>2 + 3 * 414>>>(2 + 3) * 420 - 1.3.2浮点数Python将带小数点的数字都称为浮点数: 1234>>>0.1 + 0.10.2>>>2 * 0.20.4 需要注意的是,结果包含的小数位可能是不确定的,就现在而言,暂时忽略多余的小数位即可: 1234>>>0.2 + 0.10.30000000000000004>>>3 * 0.10.30000000000000004 - 1.3.3使用函数 str() 避免错误错误例子: 123age = 23message = \"Happy \" + age + \"rd Birthday!\"print(message) 运行时会报错: 1234Traceback (most recent call last): File \"birthday.py\", line 2, in <module> message = \"Happy \" + age + \"rd Birthday!\"TypeError: must be str, not int 这是一个类型错误,意味着Python无法识别我们使用的信息。在这个例子中,Python发现我们使用了一个值为整数(int)的变量,但它不知道该如何解读这个值,这个变量表示的可能是数值23,也可能是字符2和3。像上面这样的字符串中使用整数时,需要显式地指出我们希望Python将这个整数用作字符串。为此,可调用函数 str(),它让Python将非字符串值表示为字符串: 123age = 23message = \"Happy \" + str(age) + \"rd Birthday!\"print(message) 输出结果如下: 1Happy 23rd Birthday! - 1.4注释注释让我们能够使用自然语言在程序中添加说明,Python中注释有三种方法: 123456789print(\"Hello Python!\")#这是单行注释'''这是多行注释这是多行注释'''\"\"\"这也是多行注释这也是多行注释\"\"\"","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"基础学习","slug":"Python3-学习笔记/基础学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/基础学习/"}],"tags":[{"name":"数据类型","slug":"数据类型","permalink":"https://www.itrhx.com/tags/数据类型/"},{"name":"变量","slug":"变量","permalink":"https://www.itrhx.com/tags/变量/"}]},{"title":"VMware Pro 14 安装 Ubuntu 18.04 详细教程","slug":"A06-install-ubuntu18.04","date":"2018-09-09T13:14:29.532Z","updated":"2020-03-14T05:48:49.164Z","comments":true,"path":"2018/09/09/A06-install-ubuntu18.04/","link":"","permalink":"https://www.itrhx.com/2018/09/09/A06-install-ubuntu18.04/","excerpt":"","text":"1.下载安装 VMware Workstation Pro 14 进入 VMware 官网或者在软件商店下载最新版VMware虚拟机并安装 2.下载 Ubuntu 18.04 系统 进入 Ubuntu 官网,下载最新版 Ubuntu 系统镜像 3.在 VMware 中创建虚拟机打开安装好的 VMware Workstation Pro 14,选择创建新的虚拟机 在新建虚拟机向导中选择自定义(高级) 默认直接下一步,直到出现下图,再选择稍后安装操作系统 选择客户机操作系统为 Linux ,如果你电脑是32位就选择 Ubuntu 版本,64位就选择 Ubuntu 64 位版本 更改虚拟机名称及存放位置 为虚拟机指定处理器数量,默认即可 为虚拟机分配内存,太大了可能会导致卡顿,太小了也不好,推荐内存大小即可 以下均选择默认即可 选择创建新虚拟磁盘 选择将虚拟磁盘储存为单个文件 默认下一步 点击完成 此时我们就可以在虚拟机左侧“我的计算机”下面看到刚刚创建的虚拟机 Ubuntu 64 位,单击 Ubuntu 64 位,选择“编辑虚拟机设置”, 再选择“CD/DVD(SATA)”,选择“使用ISO映像文件”,点击“浏览”,找到先前我们下载好的 Ubuntu 64 位镜像文件,点击“确定” 4.在虚拟机上安装 Ubuntu 系统单击 Ubuntu 64 位,选择“开启此虚拟机” 来到欢迎界面,选择好语言,点击“安装 Ubuntu” 选择键盘布局为“汉语” 更新和其他软件默认选择即可 安装类型选择“清除整个磁盘并安装 Ubuntu”,PS: 因为我们是新安装的系统,且在虚拟机中,所以可以选择清除整个磁盘,这个操作不会清除你原来电脑里面的东西 地区随便,在中国就行,默认即可 之后设置计算机名,密码 点击继续稍等一会就安装完成啦 安装过程中可能会出现的一些问题 1.在虚拟机上安装 Ubuntu 系统的过程中卡死不动 解决方法:关闭网络,重新安装即可 2.Ubuntu 不能全屏显示解决方法:方法①:安装 open-vm-tools: 1sudo apt-get install open-vm-tools 然后执行: 1sudo apt-get install open-vm* 重启即可全屏显示 方法②:在终端输入xrandr,并回车,我们就可以看到很多可以修改的分辨率,选择好分辨率后,比如我们要修改分辨率为 1920x1440 ,则在终端输入 xrandr -s 1920x1440,回车即可,注意 1920x1440 中间是小写字母 x,本人亲测此方法并不是很完美,不能完全适应屏幕 方法③:安装 VMware Tools:1、进入 Ubuntu 系统后,点击虚拟机上的【虚拟机】—>【安装 VMware Tools】,回到桌面即可看到一个 VMware Tools 的 图标2、复制 VMwareTools-10.0.10-4301679.tar.gz(版本根据自己的实际情况而定)到 home 目录下, 用命令 tar -xzvf VMwareTools-10.0.10-4301679.tar.gz 进行解压3、解压后 cd vmware_tools_distrib,打开终端4、输入“sudo ./vmware-install.pl”,输入用户密码后开始安装5、接下来会有很多地方需要你按 Enter或者 Yes6、当你看到出现 —the vmware team 的字样后就可以关闭窗口了,此时窗口就会自动全屏了,如果没有全屏,重启过后就可以了7、若还没有全屏显示,则将虚拟机的【查看】—>【自动调整大小】—>【自适应客户机】,都选上,即可实现全屏","categories":[{"name":"Linux","slug":"Linux","permalink":"https://www.itrhx.com/categories/Linux/"}],"tags":[{"name":"VMware","slug":"VMware","permalink":"https://www.itrhx.com/tags/VMware/"},{"name":"Ubuntu","slug":"Ubuntu","permalink":"https://www.itrhx.com/tags/Ubuntu/"}]},{"title":"主流 Markdown 编辑器推荐","slug":"A05-markdown-editor","date":"2018-08-29T15:02:46.857Z","updated":"2020-03-14T05:44:24.275Z","comments":true,"path":"2018/08/29/A05-markdown-editor/","link":"","permalink":"https://www.itrhx.com/2018/08/29/A05-markdown-editor/","excerpt":"","text":"Markdown ,2004年由 John Gruberis 设计和开发,是一种可以使用普通文本编辑器编写的标记语言,通过简单的标记语法,它可以使普通文本内容具有一定的格式,以下将介绍目前比较流行的一些 Markdown 编辑器(排名不分先后) - MarkdownPad 目前分为 MarkdownPad2 和 MarkdownPad Pro 版本,后者收费,我们使用前者足矣,用户可以通过键盘快捷键和工具栏按钮来使用或者移除 Markdown 各种语法格式,支持自定义配色方案、字体、大小和布局 、即时HTML预览、HTML和PDF导出,被很多人称赞为 Windows 平台最好用的 Markdown 编辑器,实用性强,仅支持 Windows 系统,个人觉得在 Windows 10 系统上界面并不是很好看,有时候添加音乐什么的,资源多了,实时预览会显示资源加载失败,点击此处访问 MarkdownPad 官网 - BookPad 无意间在 Microsoft Store 上发现的,完美搭配 Win10 系统,界面非常简洁漂亮,2017年9月份发布,大小30.82 MB,官方网站:https://sosfos.wordpress.com/ ,收费13人民币,可免费使用7天,各种功能应有尽有,和其他编辑器不相上下,本来想着百度百度看看有没有破解版,结果全网看不见 BookPad 的影子,估计是新出来的还不为人所知吧,可以直接在 Microsoft Store 搜索下载,或者点击链接获取:https://www.microsoft.com/store/apps/9N6P5ZH2SJSX - 小书匠 分为免费版和收费版,收费版¥20/年,其实免费版的功能已经足够强大了,多种编辑模式、多种主题选择、多种编辑器实现、丰富的语法支持、第三方同步、强大的文件管理功能,让人使用一次就爱上了它,支持 Windows 和 Web,推荐使用,点击此处访问小书匠官网 - TyporaTypora 同样支持 Windows、OS X 和 Linux,Typora 支持即时渲染技术,这也是与其他 Markdown 编辑器最显著的区别,支持数学编辑,可与 Word 直接格式转换,在 Pandoc 的支持下进行多种文档格式转换,Typora 适合那些对码字手速和排版顺畅度有要求的人群,譬如码农、网站小编等,点击此处访问 Typora 官网 - Visual Studio CodeVisual Studio Code 是众所周知的神器,是微软推出一款轻量级的文本编辑工具,类似于 Sublime,它已经默认集成 Markdown 文档编辑插件,原生就支持高亮 Markdown 的语法,但想要实时预览还需要选择 Markdown: Open Preview to the Side 命令实现,相关教程请点击此处,点击此处 访问 Visual Studio Code 官网 - MarxicoMarxico 中文名马克飞象,提供桌面客户端以及离线 Chrome App,支持移动端 Web,可以直接把文本存到印象笔记,点击此处访问 Marxico,点击此处访问 马克飞象 - Sublime Text 3Sublime Text 3 是基于 Vim 开发的跨平台代码编辑器,收费80美元,好像可以免费试用,支持 OS X、Windows、Ubuntu 等 UNIX 及 Linux 操作系统,由于其功能的多样性而广受好评,界面简约大方,定位专业,原生支持的编程语言就多达十几种,通过第三方插件,还能实现更多语法的支持,其中就包括 Markdown ,但也有个缺点,就是不能实时预览,但是用户可以通过 Markdown Preview 的插件实现对 Markdown 的预览,具体教程请点击此处查看,点击此处访问 Sublime Text 官网 - Mou Mou 是一款由国人独立开发者罗晨开发的实时预览型 Markdown 编辑器,仅支持 OS X操作系统,是目前同类应用中对汉字兼容性最好的作品,也是目前最好用的免费 Markdown 编辑器,提供语法高亮、在线预览、同步滚动、全屏模式,支持自定保存、自动匹配,允许自定义主题,支持 CSS,HTML 和 PDF 导出等功能,点击此处访问 Mou 官网 - AtomAtom 是 Github 专门为程序员推出的一个跨平台文本编辑器,具有简洁和直观的图形用户界面,并有很多有趣的特点:支持CSS,HTML,JavaScript等网页编程语言,当然也支持 Markdown ,支持宏,自动完成分屏功能,集成了文件管理器,点击此处访问 Atom 官网 - Smark国人编写的开源软件,Windows / Linux 等主流系统跨平台支持,完美支持 LaTex 数学公式、脚注、尾注等,支持使用本地 MathJax 调用,不需要在线访问 MathJax CDN,用户可配置的 Markdown 语法高亮显示,美观整洁,多种格式文件导出支持,简洁友好的界面布局,完备的各类快捷键,能极大地提高工作效率,点击此处访问 Smark 官网 - HaroopadHaroopad 覆盖三大主流桌面系统,支持 Windows、OS X 和 Linux,多种主题样式供你选择,语法标亮支持 54 种编程语言,该工具重点推荐 Ubuntu/Linux 用户使用,点击此处访问 Haroopad 官网 - CuteMarkEdCuteMarkEd 是一个基于qt5的跨平台的 Markdown 编辑器,开源的, 提供实时 HTML 预览、数学表达式、源码高亮和PDF导出,点击此处 访问 CuteMarkEd 官网 - MarkPadMarkPad 是款开源的 Markdown 编辑器,与 Window 8 风格和谐友好的界面,可以直接在你的博客或者 GitHub 中打开、保存文档,直接将图片粘贴到 Markdown 文档中,点击此处访问 MarkPad 官网 - Cmd Markdown作业部落出品,是一款不错的工具和博客平台兼顾的产品,同时支持 Linux、Mac 和 Windows 操作系统,此外还提供 Web 在线创作,社交化批注、智能云同步,最简单的方法,满足多种写作需要,点击此处访问 Cmd Markdown 官网 - FarBox同样是一款不错的 Markdown 编辑器和博客平台兼顾的产品,让用户通过Dropbox(现在默认是自己的同步服务器)直接建立个人网站。FarBox编辑器免费,同时支持 Linux、Mac 和 Windows 操作系统,Farbox服务可以免费试用,在本地编辑器内写作自动同步发布在个人博客,对于希望有个人博客但却不愿折腾的小白来说,是个不错的选择,点击此处访问 FarBox 官网 - MiuMiu 是一款 Windows 下的 Markdown 编辑器,支持 Markdown 高亮、代码高亮、即时预览,以及可以快速发布到 Github Gist,小众软件,界面美观,已经找不到官网了,小众软件网有提供百度云下载,Miu 下载地址 - MacDownMacDown 引用了许多 Mou 的设计方式,仅支持 Mac ,开源免费,点击此处访问 MacDown 官网 - Ulysses一款由国外开发商 The Soulmen 制作的 Markdown 编辑器。与其它同类应用相比,Ulysses 最大的不同在于,它能根据内置的文件管理器,以及与 iCloud 云服务器的实时同步方案,达到最快捷的文章整理效率,支持OS X , iPad,26人民币每月,14天免费试用,点击此处访问 Ulysses 官网 - Byword一款轻量级的 Markdown 编辑器,支持Mac,iPhone和iPad,界面极简,功能强大,貌似要付费使用,点击此处 访问 Byword 官网 - MaHua一个在线编辑 Markdown 文档的编辑器,小众软件,VIM 快捷键支持,完美兼容 Github 的 Markdown 语法,界面稍许简陋,点击此处访问 MaHua - Dillinger来自国外的 Markdown 编辑器,漂亮强大,支持md、 html、pdf 文件导出,支持Dropbox、Github、Google Drive、Onedrive 一键保存,点击此处访问 Dillinger - CSDN中国专业IT社区CSDN (Chinese Software Developer Network) 创立于1999年,致力于为中国软件开发者提供知识传播、在线学习、职业发展等全生命周期服务。CSDN的在线编辑器功能强大,支持导出为HTML和md文件,注册账号后即可开始创作,点击此处访问CSDN官网 - 简书简书是一个优质的创作社区,你可以在线创作并发表到社区,是国内优质原创内容输出平台,简书从一开始就已经支持 Markdown 和富文本编辑,是一个为专门为作者打造的平台,点击此处访问简书官网 要细数 Markdown 编辑器的话,可能永远也数不尽,而且每个人的看法也不同,正所谓萝卜白菜各有所爱,什么编辑器不是最重要的,重要的是我们能写出优质的文章,不断学习进步!不断提升自我! 参考资料:《好用的Markdown编辑器一览》(By:月光)《10款流行的Markdown编辑器,总有一款适合你》(By:xiaoxiao_engineer)《解决作者们的焦虑:7 款优秀 Markdown 编辑工具推荐》(By:JailJT)","categories":[{"name":"Markdown","slug":"Markdown","permalink":"https://www.itrhx.com/categories/Markdown/"}],"tags":[{"name":"Markdown","slug":"Markdown","permalink":"https://www.itrhx.com/tags/Markdown/"},{"name":"编辑器","slug":"编辑器","permalink":"https://www.itrhx.com/tags/编辑器/"}]},{"title":"Hexo 博客主题个性化","slug":"A04-Hexo-blog-topic-personalization","date":"2018-08-27T13:25:24.452Z","updated":"2020-07-13T13:29:40.312Z","comments":true,"path":"2018/08/27/A04-Hexo-blog-topic-personalization/","link":"","permalink":"https://www.itrhx.com/2018/08/27/A04-Hexo-blog-topic-personalization/","excerpt":"","text":"加入 Hexo 博客交流群:924812033,有问题互相交流学习! 原 Material X 主题现已改名为 Volantis,部分教程可能已失效,失效请留言告知,谢谢! 本文将讲述一些博客主题的美化、实用功能的添加,本文以作者 luuman 的 spfk 主题和作者 xaoxuu 的 Material X 主题为例,文章会不定时进行更新。文章涉及有关参考资料、教程、链接如有侵权请联系我删除! 本文在CSDN的链接:《Hexo 博客优化之博客美化》、《Hexo 博客优化之实用功能添加》,Hexo 博客专栏,从前期搭建到后期美化,帮您解决常见问题:《Github/Coding Pages + Hexo》,对您有帮助就点个赞吧❤️ 请注意:不同主题可能方法有些不同,相同主题不同版本,配置方法也有所差异! 博客美化前提条件:有一定的前端基础,了解 HTML、CSS、JS,了解 CSS 预处理语言 Sass、Less、Stylus,搞懂 hexo 的目录结构。 博客美化通用步骤:选定主题,认真阅读主题文档,分析主题目录结构,了解每个文件是对应网页哪个部分的,认真阅读美化教程,美化教程本质上只为你提供核心代码和思路,具体代码要添加到哪个地方,需要你自己搞懂主题结构,添加到需要的、合适的位置! 博客美化终极奥秘:创作第一,体验第二,避免繁杂,简洁为上! 【01】添加评论系统 主流的评论系统有很多,比如:网易云跟帖、多说、友言、畅言、来必力(LiveRe)、Disqus、Valine、Gitment等等,目前网易云跟帖、多说、友言都已经关闭了,还有些可能需要翻墙,比较麻烦,百度了一下,最后还是选择了来必力评论系统 进入来必力官网,注册一个账号(注册时可能需要翻墙) 注册完毕之后,登录,进入安装页面,选择 City 免费版安装,安装之后你会得到一段代码 我们打开主题文件下的 _config.yml 文件,添加如下代码: 在 \\themes\\hexo-theme-spfk\\layout\\_partial\\comments 文件夹下新建一个 livere.ejs 的文件,在里面填写来必力提供的代码: 123456789101112131415161718<!-- 来必力City版安装代码 --><div id=\"lv-container\" data-id=\"city\" data-uid=\"这里是你的uid\"> <script type=\"text/javascript\"> (function(d, s) { var j, e = d.getElementsByTagName(s)[0]; if (typeof LivereTower === 'function') { return; } j = d.createElement(s); j.src = 'https://cdn-city.livere.com/js/embed.dist.js'; j.async = true; e.parentNode.insertBefore(j, e); })(document, 'script'); </script> <noscript>为正常使用来必力评论功能请激活JavaScript</noscript></div><!-- City版安装代码已完成 --> 打开 \\themes\\hexo-theme-spfk\\layout\\_partial\\article.ejs 文件,在适当位置添加如下红框中的代码: 完成以上操作之后,我们就可以使用来必力评论系统了 【02】添加卡通人物 我在逛别人博客的时候偶然发现右下角居然有一个萌萌的卡通人物,还能根据你鼠标位置摇头,瞬间被吸引到了,赶紧也给自己博客添加一个吧!点击此处进入该项目地址 输入如下命令获取 live2d : 1$ npm install --save hexo-helper-live2d 输入以下命令,下载相应的模型,将 packagename 更换成模型名称即可,更多模型选择请点击此处,各个模型的预览请访问原作者的博客 1$ npm install packagename 打开站点目录下的 _config.yml 文件,添加如下代码: 1234567891011live2d: enable: true scriptFrom: local model: use: live2d-widget-model-haruto #模型选择 display: position: right #模型位置 width: 150 #模型宽度 height: 300 #模型高度 mobile: show: false #是否在手机端显示 设置好过后我们就拥有了一个卡通人物 【03】自定义鼠标指针样式 在 \\themes\\material-x\\source\\less\\_base.less 文件 body 样式里写入如下代码: 123456body { cursor: url(https://cdn.jsdelivr.net/gh/TRHX/CDN-for-itrhx.com@2.1.6/images/mouse.cur),auto; background-color: @theme_background; ...... ......} 鼠标指针可以用 Axialis CursorWorkshop 这个软件自己制作,不同主题具体放的文件有所不同,确保在博客主体 body 的 CSS 文件中即可,其中的鼠标指针链接可替换成自己的,首先尝试加载 https://cdn.jsdelivr.net/gh/TRHX/CDN-for-itrhx.com@2.1.6/images/mouse.cur ,如果该文件不存在或由于其他原因无效,那么 auto 会被使用,也就是自动默认效果,图片格式为.ico、.ani、.cur,建议使用.cur,如果使用.ani或者其他格式无效,原因是浏览器兼容问题,请阅读参考文档或者参考以下兼容表: 浏览器 最低版本 格式 Internet Explorer 6.0 .cur / .ani Firefox (Gecko), Windows and Linux 1.5 (1.8) .cur / .png / .gif / .jpg Firefox (Gecko) 4.0 (2.0) .cur / .png / .gif / .jpg / .svg Opera — — Safari (Webkit) 3.0 (522-523) .cur / .png / .gif / .jpg 拓展阅读:《CSS 鼠标样式 cursor属性》 (By:歪脖先生的博客) 【04】添加鼠标点击爱心效果 在 \\themes\\hexo-theme-spfk\\source\\js 下新建文件 love.js,在 love.js 文件中添加以下代码: 1!function(e,t,a){function n(){c(\".heart{width: 10px;height: 10px;position: fixed;background: #f00;transform: rotate(45deg);-webkit-transform: rotate(45deg);-moz-transform: rotate(45deg);}.heart:after,.heart:before{content: '';width: inherit;height: inherit;background: inherit;border-radius: 50%;-webkit-border-radius: 500%;-moz-border-radius: 50%;position: fixed;}.heart:after{top: -5px;}.heart:before{left: -5px;}\"),o(),r()}function r(){for(var e=0;e<d.length;e++)d[e].alpha<=0?(t.body.removeChild(d[e].el),d.splice(e,1)):(d[e].y--,d[e].scale+=.004,d[e].alpha-=.013,d[e].el.style.cssText=\"left:\"+d[e].x+\"px;top:\"+d[e].y+\"px;opacity:\"+d[e].alpha+\";transform:scale(\"+d[e].scale+\",\"+d[e].scale+\") rotate(45deg);background:\"+d[e].color+\";z-index:99999\");requestAnimationFrame(r)}function o(){var t=\"function\"==typeof e.onclick&&e.onclick;e.onclick=function(e){t&&t(),i(e)}}function i(e){var a=t.createElement(\"div\");a.className=\"heart\",d.push({el:a,x:e.clientX-5,y:e.clientY-5,scale:1,alpha:1,color:s()}),t.body.appendChild(a)}function c(e){var a=t.createElement(\"style\");a.type=\"text/css\";try{a.appendChild(t.createTextNode(e))}catch(t){a.styleSheet.cssText=e}t.getElementsByTagName(\"head\")[0].appendChild(a)}function s(){return\"rgb(\"+~~(255*Math.random())+\",\"+~~(255*Math.random())+\",\"+~~(255*Math.random())+\")\"}var d=[];e.requestAnimationFrame=function(){return e.requestAnimationFrame||e.webkitRequestAnimationFrame||e.mozRequestAnimationFrame||e.oRequestAnimationFrame||e.msRequestAnimationFrame||function(e){setTimeout(e,1e3/60)}}(),n()}(window,document); 在 \\themes\\hexo-theme-spfk\\layout\\layout.ejs 文件末尾添加以下代码: 12<!-- 页面点击小红心 --><script type=\"text/javascript\" src=\"/js/love.js\"></script> 完成以上操作后,当我们点击鼠标的时候就可以看见爱心的特效了 【05】添加鼠标点击显示字体效果 在 \\themes\\hexo-theme-spfk\\source\\js 下新建文件 click_show_text.js,在 click_show_text.js 文件中添加以下代码: 123456789101112131415161718192021222324252627282930313233var a_idx = 0;jQuery(document).ready(function($) { $(\"body\").click(function(e) { var a = new Array (\"富强\", \"民主\", \"文明\", \"和谐\", \"自由\", \"平等\", \"公正\", \"法治\", \"爱国\", \"敬业\", \"诚信\", \"友善\"); var $i = $(\"<span/>\").text(a[a_idx]); a_idx = (a_idx + 1) % a.length; var x = e.pageX, y = e.pageY; $i.css({ \"z-index\": 5, \"top\": y - 20, \"left\": x, \"position\": \"absolute\", \"font-weight\": \"bold\", \"color\": \"#FF0000\" }); $(\"body\").append($i); $i.animate({ \"top\": y - 180, \"opacity\": 0 }, 3000, function() { $i.remove(); }); }); setTimeout('delay()', 2000);});function delay() { $(\".buryit\").removeAttr(\"onclick\");} 其中的社会主义核心价值观可以根据你自己的创意替换为其他文字 如果想要每次点击显示的文字为不同颜色,可以将其中 color 值进行如下更改: 1\"color\": \"rgb(\" + ~~(255 * Math.random()) + \",\" + ~~(255 * Math.random()) + \",\" + ~~(255 * Math.random()) + \")\" 然后在 \\themes\\hexo-theme-spfk\\layout\\layout.ejs 文件末尾添加以下代码: 12<!--单击显示文字--><script type=\"text/javascript\" src=\"/js/click_show_text.js\"></script> 最终实现效果如下: 【06】添加鼠标点击烟花爆炸效果 在 \\themes\\material-x\\source\\js 目录下新建一个 fireworks.js 的文件,里面写入以下代码: 1\"use strict\";function updateCoords(e){pointerX=(e.clientX||e.touches[0].clientX)-canvasEl.getBoundingClientRect().left,pointerY=e.clientY||e.touches[0].clientY-canvasEl.getBoundingClientRect().top}function setParticuleDirection(e){var t=anime.random(0,360)*Math.PI/180,a=anime.random(50,180),n=[-1,1][anime.random(0,1)]*a;return{x:e.x+n*Math.cos(t),y:e.y+n*Math.sin(t)}}function createParticule(e,t){var a={};return a.x=e,a.y=t,a.color=colors[anime.random(0,colors.length-1)],a.radius=anime.random(16,32),a.endPos=setParticuleDirection(a),a.draw=function(){ctx.beginPath(),ctx.arc(a.x,a.y,a.radius,0,2*Math.PI,!0),ctx.fillStyle=a.color,ctx.fill()},a}function createCircle(e,t){var a={};return a.x=e,a.y=t,a.color=\"#F00\",a.radius=0.1,a.alpha=0.5,a.lineWidth=6,a.draw=function(){ctx.globalAlpha=a.alpha,ctx.beginPath(),ctx.arc(a.x,a.y,a.radius,0,2*Math.PI,!0),ctx.lineWidth=a.lineWidth,ctx.strokeStyle=a.color,ctx.stroke(),ctx.globalAlpha=1},a}function renderParticule(e){for(var t=0;t<e.animatables.length;t++){e.animatables[t].target.draw()}}function animateParticules(e,t){for(var a=createCircle(e,t),n=[],i=0;i<numberOfParticules;i++){n.push(createParticule(e,t))}anime.timeline().add({targets:n,x:function(e){return e.endPos.x},y:function(e){return e.endPos.y},radius:0.1,duration:anime.random(1200,1800),easing:\"easeOutExpo\",update:renderParticule}).add({targets:a,radius:anime.random(80,160),lineWidth:0,alpha:{value:0,easing:\"linear\",duration:anime.random(600,800)},duration:anime.random(1200,1800),easing:\"easeOutExpo\",update:renderParticule,offset:0})}function debounce(e,t){var a;return function(){var n=this,i=arguments;clearTimeout(a),a=setTimeout(function(){e.apply(n,i)},t)}}var canvasEl=document.querySelector(\".fireworks\");if(canvasEl){var ctx=canvasEl.getContext(\"2d\"),numberOfParticules=30,pointerX=0,pointerY=0,tap=\"mousedown\",colors=[\"#FF1461\",\"#18FF92\",\"#5A87FF\",\"#FBF38C\"],setCanvasSize=debounce(function(){canvasEl.width=2*window.innerWidth,canvasEl.height=2*window.innerHeight,canvasEl.style.width=window.innerWidth+\"px\",canvasEl.style.height=window.innerHeight+\"px\",canvasEl.getContext(\"2d\").scale(2,2)},500),render=anime({duration:1/0,update:function(){ctx.clearRect(0,0,canvasEl.width,canvasEl.height)}});document.addEventListener(tap,function(e){\"sidebar\"!==e.target.id&&\"toggle-sidebar\"!==e.target.id&&\"A\"!==e.target.nodeName&&\"IMG\"!==e.target.nodeName&&(render.play(),updateCoords(e),animateParticules(pointerX,pointerY))},!1),setCanvasSize(),window.addEventListener(\"resize\",setCanvasSize,!1)}\"use strict\";function updateCoords(e){pointerX=(e.clientX||e.touches[0].clientX)-canvasEl.getBoundingClientRect().left,pointerY=e.clientY||e.touches[0].clientY-canvasEl.getBoundingClientRect().top}function setParticuleDirection(e){var t=anime.random(0,360)*Math.PI/180,a=anime.random(50,180),n=[-1,1][anime.random(0,1)]*a;return{x:e.x+n*Math.cos(t),y:e.y+n*Math.sin(t)}}function createParticule(e,t){var a={};return a.x=e,a.y=t,a.color=colors[anime.random(0,colors.length-1)],a.radius=anime.random(16,32),a.endPos=setParticuleDirection(a),a.draw=function(){ctx.beginPath(),ctx.arc(a.x,a.y,a.radius,0,2*Math.PI,!0),ctx.fillStyle=a.color,ctx.fill()},a}function createCircle(e,t){var a={};return a.x=e,a.y=t,a.color=\"#F00\",a.radius=0.1,a.alpha=0.5,a.lineWidth=6,a.draw=function(){ctx.globalAlpha=a.alpha,ctx.beginPath(),ctx.arc(a.x,a.y,a.radius,0,2*Math.PI,!0),ctx.lineWidth=a.lineWidth,ctx.strokeStyle=a.color,ctx.stroke(),ctx.globalAlpha=1},a}function renderParticule(e){for(var t=0;t<e.animatables.length;t++){e.animatables[t].target.draw()}}function animateParticules(e,t){for(var a=createCircle(e,t),n=[],i=0;i<numberOfParticules;i++){n.push(createParticule(e,t))}anime.timeline().add({targets:n,x:function(e){return e.endPos.x},y:function(e){return e.endPos.y},radius:0.1,duration:anime.random(1200,1800),easing:\"easeOutExpo\",update:renderParticule}).add({targets:a,radius:anime.random(80,160),lineWidth:0,alpha:{value:0,easing:\"linear\",duration:anime.random(600,800)},duration:anime.random(1200,1800),easing:\"easeOutExpo\",update:renderParticule,offset:0})}function debounce(e,t){var a;return function(){var n=this,i=arguments;clearTimeout(a),a=setTimeout(function(){e.apply(n,i)},t)}}var canvasEl=document.querySelector(\".fireworks\");if(canvasEl){var ctx=canvasEl.getContext(\"2d\"),numberOfParticules=30,pointerX=0,pointerY=0,tap=\"mousedown\",colors=[\"#FF1461\",\"#18FF92\",\"#5A87FF\",\"#FBF38C\"],setCanvasSize=debounce(function(){canvasEl.width=2*window.innerWidth,canvasEl.height=2*window.innerHeight,canvasEl.style.width=window.innerWidth+\"px\",canvasEl.style.height=window.innerHeight+\"px\",canvasEl.getContext(\"2d\").scale(2,2)},500),render=anime({duration:1/0,update:function(){ctx.clearRect(0,0,canvasEl.width,canvasEl.height)}});document.addEventListener(tap,function(e){\"sidebar\"!==e.target.id&&\"toggle-sidebar\"!==e.target.id&&\"A\"!==e.target.nodeName&&\"IMG\"!==e.target.nodeName&&(render.play(),updateCoords(e),animateParticules(pointerX,pointerY))},!1),setCanvasSize(),window.addEventListener(\"resize\",setCanvasSize,!1)}; 然后在 \\themes\\material-x\\layout\\layout.ejs 文件中写入以下代码: 123<canvas class=\"fireworks\" style=\"position: fixed;left: 0;top: 0;z-index: 1; pointer-events: none;\" ></canvas> <script type=\"text/javascript\" src=\"//cdn.bootcss.com/animejs/2.2.0/anime.min.js\"></script> <script type=\"text/javascript\" src=\"/js/fireworks.js\"></script> 最终效果: 【07】添加彩色滚动变换字体 在你想要添加彩色滚动变换字体的地方写入以下代码即可,其中文字可自行更改: 123456789101112131415161718192021222324252627282930313233343536373839404142<div id=\"binft\"></div> <script> var binft = function (r) { function t() { return b[Math.floor(Math.random() * b.length)] } function e() { return String.fromCharCode(94 * Math.random() + 33) } function n(r) { for (var n = document.createDocumentFragment(), i = 0; r > i; i++) { var l = document.createElement(\"span\"); l.textContent = e(), l.style.color = t(), n.appendChild(l) } return n } function i() { var t = o[c.skillI]; c.step ? c.step-- : (c.step = g, c.prefixP < l.length ? (c.prefixP >= 0 && (c.text += l[c.prefixP]), c.prefixP++) : \"forward\" === c.direction ? c.skillP < t.length ? (c.text += t[c.skillP], c.skillP++) : c.delay ? c.delay-- : (c.direction = \"backward\", c.delay = a) : c.skillP > 0 ? (c.text = c.text.slice(0, -1), c.skillP--) : (c.skillI = (c.skillI + 1) % o.length, c.direction = \"forward\")), r.textContent = c.text, r.appendChild(n(c.prefixP < l.length ? Math.min(s, s + c.prefixP) : Math.min(s, t.length - c.skillP))), setTimeout(i, d) } var l = \"\", o = [\"青青陵上柏,磊磊涧中石。\", \"人生天地间,忽如远行客。\",\"斗酒相娱乐,聊厚不为薄。\", \"驱车策驽马,游戏宛与洛。\",\"洛中何郁郁,冠带自相索。\",\"长衢罗夹巷,王侯多第宅。\",\"两宫遥相望,双阙百余尺。\",\"极宴娱心意,戚戚何所迫?\"].map(function (r) { return r + \"\" }), a = 2, g = 1, s = 5, d = 75, b = [\"rgb(110,64,170)\", \"rgb(150,61,179)\", \"rgb(191,60,175)\", \"rgb(228,65,157)\", \"rgb(254,75,131)\", \"rgb(255,94,99)\", \"rgb(255,120,71)\", \"rgb(251,150,51)\", \"rgb(226,183,47)\", \"rgb(198,214,60)\", \"rgb(175,240,91)\", \"rgb(127,246,88)\", \"rgb(82,246,103)\", \"rgb(48,239,130)\", \"rgb(29,223,163)\", \"rgb(26,199,194)\", \"rgb(35,171,216)\", \"rgb(54,140,225)\", \"rgb(76,110,219)\", \"rgb(96,84,200)\"], c = { text: \"\", prefixP: -s, skillI: 0, skillP: 0, direction: \"forward\", delay: a, step: g }; i() }; binft(document.getElementById('binft')); </script> 最终效果: 【08】添加字数统计和阅读时长 先在博客目录下执行以下命令安装 hexo-wordcount 插件: 1$ npm i --save hexo-wordcount 注意:在 Material X 主题中,字数统计和阅读时长的功能我已提交 PR,在最新版本中,只需要安装插件后,在主题 config.yml 配置文件里,将 word_count 关键字设置为 true 即可,对于旧版本,可以通过以下方法实现: 以 Material X 主题(版本 1.2.1)为例,在 \\themes\\material-x\\layout\\_meta 目录下创建 word.ejs 文件,在 word.ejs 文件中写入以下代码: 123456789101112131415161718192021<% if(isPostList || !isPostList){ %> <% if (theme.word_count && !post.no_word_count) { %> <div style=\"margin-right: 10px;\"> <span class=\"post-time\"> <span class=\"post-meta-item-icon\"> <i class=\"fa fa-keyboard\"></i> <span class=\"post-meta-item-text\"> 字数统计: </span> <span class=\"post-count\"><%= wordcount(post.content) %>字</span> </span> </span> &nbsp; | &nbsp; <span class=\"post-time\"> <span class=\"post-meta-item-icon\"> <i class=\"fa fa-hourglass-half\"></i> <span class=\"post-meta-item-text\"> 阅读时长≈</span> <span class=\"post-count\"><%= min2read(post.content) %>分</span> </span> </span> </div> <% } %><% } %> 然后在主题的配置文件 _config.yml 找到 meta 关键字,将 word 填入 header 中: 123meta: header: [title, author, date, categories, tags, counter, word, top] footer: [updated, share] 最后在主题目录下的 _config.yml 添加以下配置即可 1word_count: true 效果图: 同样的,以 spfk 主题为例,在 \\themes\\hexo-theme-spfk\\layout\\_partial\\post 目录下创建 word.ejs 文件,在 word.ejs 文件中写入以下代码: 1234567891011121314151617<div style=\"margin-top:10px;\"> <span class=\"post-time\"> <span class=\"post-meta-item-icon\"> <i class=\"fa fa-keyboard-o\"></i> <span class=\"post-meta-item-text\"> 字数统计: </span> <span class=\"post-count\"><%= wordcount(post.content) %>字</span> </span> </span> &nbsp; | &nbsp; <span class=\"post-time\"> <span class=\"post-meta-item-icon\"> <i class=\"fa fa-hourglass-half\"></i> <span class=\"post-meta-item-text\"> 阅读时长: </span> <span class=\"post-count\"><%= min2read(post.content) %>分</span> </span> </span></div> 然后在 \\themes\\hexo-theme-spfk\\layout\\_partial\\article.ejs 中适当位置添加以下代码: 最后在主题目录下的 _config.yml 添加以下配置 1word_count: true 如果显示的位置不好,可以自行更改其位置,成功配置后的效果如下: 另外:要在博客底部显示所有文章的总字数,可以点击此处,根据你博客底部文件的类型选择相应的代码放在适当的位置即可,前提是要安装好 hexo-wordcount 插件,例如我使用 Material X 主题,在 \\themes\\material-x\\layout\\_partial 目录下的 footer.ejs 文件中添加如下代码: 12<i class=\"fas fa-chart-area\"></i><span class=\"post-count\">字数统计:<%= totalcount(site) %></span> 实现效果如下: 【09】添加背景音乐 打开网页版网易云音乐,选择你准备添加的背景音乐,点击生成外链播放器,前提是要有版权,不然是无法生成外链播放器的,复制底下的HTML代码 然后将此代码放到你想要放的地方,比如放在博客的左侧,则打开 \\themes\\hexo-theme-spfk\\layout\\_partial\\left-col.ejs 文件,将复制的HTML代码粘贴进去,再进行适当的位置设置让播放器更美观,其中 auto=1 表示打开网页自动播放音乐,auto=0 表示关闭自动播放音乐 最后效果如下: 这种网易云音乐外链的方式有很多局限性,因此推荐使用aplayer,GitHub地址为:https://github.com/MoePlayer/APlayer ,参考教程:《hexo上的aplayer应用》 【10】添加网站运行时间 一个比较好的小功能,可以看见自己的博客运行多久了,时间一天天的增加,成就感也会一天天增加的在 \\themes\\hexo-theme-spfk\\layout\\_partial\\footer.ejs 文件下添加以下代码: 1234567891011121314151617<span id=\"timeDate\">载入天数...</span><span id=\"times\">载入时分秒...</span><script> var now = new Date(); function createtime() { var grt= new Date(\"08/10/2018 17:38:00\");//在此处修改你的建站时间,格式:月/日/年 时:分:秒 now.setTime(now.getTime()+250); days = (now - grt ) / 1000 / 60 / 60 / 24; dnum = Math.floor(days); hours = (now - grt ) / 1000 / 60 / 60 - (24 * dnum); hnum = Math.floor(hours); if(String(hnum).length ==1 ){hnum = \"0\" + hnum;} minutes = (now - grt ) / 1000 /60 - (24 * 60 * dnum) - (60 * hnum); mnum = Math.floor(minutes); if(String(mnum).length ==1 ){mnum = \"0\" + mnum;} seconds = (now - grt ) / 1000 - (24 * 60 * 60 * dnum) - (60 * 60 * hnum) - (60 * mnum); snum = Math.round(seconds); if(String(snum).length ==1 ){snum = \"0\" + snum;} document.getElementById(\"timeDate\").innerHTML = \"本站已安全运行 \"+dnum+\" 天 \"; document.getElementById(\"times\").innerHTML = hnum + \" 小时 \" + mnum + \" 分 \" + snum + \" 秒\"; } setInterval(\"createtime()\",250);</script> 最后效果如下: 【11】添加百度统计 百度统计是百度推出的一款免费的专业网站流量分析工具,能够告诉用户访客是如何找到并浏览用户的网站,在网站上做了些什么,非常有趣,接下来我们把百度统计添加到自己博客当中 访问百度统计首页,注册一个账号后登陆,添加你的博客网站 接着点击代码获取,复制该代码 然后到目录 \\Hexo\\themes\\hexo-theme-spfk\\layout\\_partial 下新建一个 baidu-analytics.ejs 文件,里面粘贴你刚刚复制的代码 修改主题文件夹下的 _config.yml 文件,将你的key(图中涂掉部分)填写进去: 所有操作完成后可以在百度统计管理页面检查代码是否安装成功,如果代码安装正确,一般20分钟后,可以查看网站分析数据 另外推荐:友盟,2010年4月在北京成立,安全、可靠、公正、第三方的网站流量统计分析系统 【12】浏览器网页标题恶搞当用户访问你的博客时点击到了其他网页,我们可以恶搞一下网页标题,呼唤用户回来,首先在目录 \\themes\\material-x\\source\\js 下新建一个 FunnyTitle.js 文件,在里面填写如下代码: 1234567891011121314151617// 浏览器搞笑标题var OriginTitle = document.title;var titleTime;document.addEventListener('visibilitychange', function () { if (document.hidden) { $('[rel=\"icon\"]').attr('href', \"/funny.ico\"); document.title = '╭(°A°`)╮ 页面崩溃啦 ~'; clearTimeout(titleTime); } else { $('[rel=\"icon\"]').attr('href', \"/favicon.ico\"); document.title = '(ฅ>ω<*ฅ) 噫又好啦 ~' + OriginTitle; titleTime = setTimeout(function () { document.title = OriginTitle; }, 2000); }}); 其中 funny.ico 是用户切换到其他标签后你网站的图标,favicon.ico 是正常图标,然后在 \\themes\\material-x\\layout\\layout.ejs 文件中添加如下代码: 12<!--浏览器搞笑标题--><script type=\"text/javascript\" src=\"/js/FunnyTitle.js\"></script> 再次部署博客后就可以看见标题搞笑的效果了: 【13】背景添加动态线条效果 在 \\Hexo\\themes\\hexo-theme-spfk\\layout\\layout.ejs 文件中添加如下代码: 1234<!--动态线条背景--><script type=\"text/javascript\"color=\"220,220,220\" opacity='0.7' zIndex=\"-2\" count=\"200\" src=\"//cdn.bootcss.com/canvas-nest.js/1.0.0/canvas-nest.min.js\"></script> 其中: color:表示线条颜色,三个数字分别为(R,G,B),默认:(0,0,0) opacity:表示线条透明度(0~1),默认:0.5 count:表示线条的总数量,默认:150 zIndex:表示背景的z-index属性,css属性用于控制所在层的位置,默认:-1 最终实现效果: 【14】添加人体时钟 无意中发现了个有趣的人体时钟 HONE HONE CLOCK,作者是个日本人,点击此处访问作者博客,点击此处在作者原博客上查看动态样式,点击此处查看动态大图,如果你的博客上有合适的地方,加上一个人体时钟会很有趣的 实现代码: 12345<!--人体时钟背景透明--><script charset=\"Shift_JIS\" src=\"http://chabudai.sakura.ne.jp/blogparts/honehoneclock/honehone_clock_tr.js\"></script><!--人体时钟背景白--><script charset=\"Shift_JIS\" src=\"http://chabudai.sakura.ne.jp/blogparts/honehoneclock/honehone_clock_wh.js\"></script> 其他网页小挂件推荐: http://abowman.com/ 里面有很多有趣的小挂件,可以养养鱼、龟、狗、仓鼠等各式各样的虚拟宠物,能根据你的鼠标指针位置移动,直接复制代码就可以用 http://www.revolvermaps.com/ 它提供网站访客地理信息,可以以2D、3D等形式显示 http://www.amazingcounters.com/ 免费网站计数器,有非常多的样式供你选择,可以设置计数器初始数值,可以设置按访问量计数,也可以按独立访问者计数 https://www.seniverse.com/widget/get 心知天气提供基于Web的免费天气插件,可以为你的网站添加一项简洁美观的天气预报功能,并自动适配PC和手机上的浏览 【15】添加RSS订阅 RSS订阅是站点用来和其他站点之间共享内容的一种简易方式,即Really Simple Syndication(简易信息聚合),如果不会使用,可以参见百度百科:https://baike.baidu.com/item/RSS%E8%AE%A2%E9%98%85/663114 ;首先我们安装feed插件,在本地hexo目录下右键git bash here,输入以下命令: 1$ npm install hexo-generator-feed 等待安装完成后,打开hexo目录下配置文件的_config.yml,在末尾添加以下配置: 12345678910# Extensions## Plugins: http://hexo.io/plugins/#RSS订阅plugin:- hexo-generator-feed#Feed Atomfeed:type: atompath: atom.xmllimit: 20 随后打开主题配置文件_config.yml,添加以下配置: 1rss: /atom.xml 至此,RSS订阅功能添加完成 【16】添加网站雪花飘落效果 样式一和样式二分别如下: 实现方法:在 \\Hexo\\themes\\hexo-theme-spfk\\source\\js 目录下新建一个 snow.js 文件,粘贴以下代码: 123456789101112131415161718192021222324252627282930313233343536373839404142/*样式一*/(function($){ $.fn.snow = function(options){ var $flake = $('<div id=\"snowbox\" />').css({'position': 'absolute','z-index':'9999', 'top': '-50px'}).html('&#10052;'), documentHeight = $(document).height(), documentWidth = $(document).width(), defaults = { minSize : 10, maxSize : 20, newOn : 1000, flakeColor : \"#AFDAEF\" /* 此处可以定义雪花颜色,若要白色可以改为#FFFFFF */ }, options = $.extend({}, defaults, options); var interval= setInterval( function(){ var startPositionLeft = Math.random() * documentWidth - 100, startOpacity = 0.5 + Math.random(), sizeFlake = options.minSize + Math.random() * options.maxSize, endPositionTop = documentHeight - 200, endPositionLeft = startPositionLeft - 500 + Math.random() * 500, durationFall = documentHeight * 10 + Math.random() * 5000; $flake.clone().appendTo('body').css({ left: startPositionLeft, opacity: startOpacity, 'font-size': sizeFlake, color: options.flakeColor }).animate({ top: endPositionTop, left: endPositionLeft, opacity: 0.2 },durationFall,'linear',function(){ $(this).remove() }); }, options.newOn); };})(jQuery);$(function(){ $.fn.snow({ minSize: 5, /* 定义雪花最小尺寸 */ maxSize: 50,/* 定义雪花最大尺寸 */ newOn: 300 /* 定义密集程度,数字越小越密集 */ });}); 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128/*样式二*//* 控制下雪 */function snowFall(snow) { /* 可配置属性 */ snow = snow || {}; this.maxFlake = snow.maxFlake || 200; /* 最多片数 */ this.flakeSize = snow.flakeSize || 10; /* 雪花形状 */ this.fallSpeed = snow.fallSpeed || 1; /* 坠落速度 */}/* 兼容写法 */requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame || window.oRequestAnimationFrame || function(callback) { setTimeout(callback, 1000 / 60); };cancelAnimationFrame = window.cancelAnimationFrame || window.mozCancelAnimationFrame || window.webkitCancelAnimationFrame || window.msCancelAnimationFrame || window.oCancelAnimationFrame;/* 开始下雪 */snowFall.prototype.start = function(){ /* 创建画布 */ snowCanvas.apply(this); /* 创建雪花形状 */ createFlakes.apply(this); /* 画雪 */ drawSnow.apply(this)}/* 创建画布 */function snowCanvas() { /* 添加Dom结点 */ var snowcanvas = document.createElement(\"canvas\"); snowcanvas.id = \"snowfall\"; snowcanvas.width = window.innerWidth; snowcanvas.height = document.body.clientHeight; snowcanvas.setAttribute(\"style\", \"position:absolute; top: 0; left: 0; z-index: 1; pointer-events: none;\"); document.getElementsByTagName(\"body\")[0].appendChild(snowcanvas); this.canvas = snowcanvas; this.ctx = snowcanvas.getContext(\"2d\"); /* 窗口大小改变的处理 */ window.onresize = function() { snowcanvas.width = window.innerWidth; /* snowcanvas.height = window.innerHeight */ }}/* 雪运动对象 */function flakeMove(canvasWidth, canvasHeight, flakeSize, fallSpeed) { this.x = Math.floor(Math.random() * canvasWidth); /* x坐标 */ this.y = Math.floor(Math.random() * canvasHeight); /* y坐标 */ this.size = Math.random() * flakeSize + 2; /* 形状 */ this.maxSize = flakeSize; /* 最大形状 */ this.speed = Math.random() * 1 + fallSpeed; /* 坠落速度 */ this.fallSpeed = fallSpeed; /* 坠落速度 */ this.velY = this.speed; /* Y方向速度 */ this.velX = 0; /* X方向速度 */ this.stepSize = Math.random() / 30; /* 步长 */ this.step = 0 /* 步数 */}flakeMove.prototype.update = function() { var x = this.x, y = this.y; /* 左右摆动(余弦) */ this.velX *= 0.98; if (this.velY <= this.speed) { this.velY = this.speed } this.velX += Math.cos(this.step += .05) * this.stepSize; this.y += this.velY; this.x += this.velX; /* 飞出边界的处理 */ if (this.x >= canvas.width || this.x <= 0 || this.y >= canvas.height || this.y <= 0) { this.reset(canvas.width, canvas.height) }};/* 飞出边界-放置最顶端继续坠落 */flakeMove.prototype.reset = function(width, height) { this.x = Math.floor(Math.random() * width); this.y = 0; this.size = Math.random() * this.maxSize + 2; this.speed = Math.random() * 1 + this.fallSpeed; this.velY = this.speed; this.velX = 0;};// 渲染雪花-随机形状(此处可修改雪花颜色!!!)flakeMove.prototype.render = function(ctx) { var snowFlake = ctx.createRadialGradient(this.x, this.y, 0, this.x, this.y, this.size); snowFlake.addColorStop(0, \"rgba(255, 255, 255, 0.9)\"); /* 此处是雪花颜色,默认是白色 */ snowFlake.addColorStop(.5, \"rgba(255, 255, 255, 0.5)\"); /* 若要改为其他颜色,请自行查 */ snowFlake.addColorStop(1, \"rgba(255, 255, 255, 0)\"); /* 找16进制的RGB 颜色代码。 */ ctx.save(); ctx.fillStyle = snowFlake; ctx.beginPath(); ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2); ctx.fill(); ctx.restore();};/* 创建雪花-定义形状 */function createFlakes() { var maxFlake = this.maxFlake, flakes = this.flakes = [], canvas = this.canvas; for (var i = 0; i < maxFlake; i++) { flakes.push(new flakeMove(canvas.width, canvas.height, this.flakeSize, this.fallSpeed)) }}/* 画雪 */function drawSnow() { var maxFlake = this.maxFlake, flakes = this.flakes; ctx = this.ctx, canvas = this.canvas, that = this; /* 清空雪花 */ ctx.clearRect(0, 0, canvas.width, canvas.height); for (var e = 0; e < maxFlake; e++) { flakes[e].update(); flakes[e].render(ctx); } /* 一帧一帧的画 */ this.loop = requestAnimationFrame(function() { drawSnow.apply(that); });}/* 调用及控制方法 */var snow = new snowFall({maxFlake:60});snow.start(); 然后在 \\Hexo\\themes\\hexo-theme-spfk\\layout\\layout.ejs 文件里引用即可: 12<!-- 雪花特效 --><script type=\"text/javascript\" src=\"\\js\\snow.js\"></script> 如果没效果,请确认网页是否已载入JQurey,如果没有请在下雪代码之前引入JQ即可: 12<script type=\"text/javascript\" src=\"http://libs.baidu.com/jquery/1.8.3/jquery.js\"></script><script type=\"text/javascript\" src=\"http://libs.baidu.com/jquery/1.8.3/jquery.min.js\"></script> 原文链接:《分享两种圣诞节雪花特效JS代码(网站下雪效果)》 【17】添加 Fork me on GitHub 效果 效果图: 点击此处可以查看更多样式,将相应样式的代码复制到你想要放的地方就OK了,代码里的链接也要替换成你的,更多创意,比如 Follow me on CSDN ,只需要用PS改掉图片里的文字,替换掉相应链接即可 【18】添加背景动态彩带效果 样式一是鼠标点击后彩带自动更换样式,样式二是飘动的彩带: 实现方法:在 \\themes\\material-x\\layout\\layout.ejs 文件的body前面添加如下代码: 12<!-- 样式一(鼠标点击更换样式) --><script src=\"https://g.joyinshare.com/hc/ribbon.min.js\" type=\"text/javascript\"></script> 12<!-- 样式二(飘动的彩带) --><script src=\"https://g.joyinshare.com/hc/piao.js\" type=\"text/javascript\"></script> 【19】添加背景代码雨特效 新建 DigitalRain.js,写入以下代码: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657window.onload = function(){ //获取画布对象 var canvas = document.getElementById(\"canvas\"); //获取画布的上下文 var context =canvas.getContext(\"2d\"); var s = window.screen; var W = canvas.width = s.width; var H = canvas.height; //获取浏览器屏幕的宽度和高度 //var W = window.innerWidth; //var H = window.innerHeight; //设置canvas的宽度和高度 canvas.width = W; canvas.height = H; //每个文字的字体大小 var fontSize = 12; //计算列 var colunms = Math.floor(W /fontSize); //记录每列文字的y轴坐标 var drops = []; //给每一个文字初始化一个起始点的位置 for(var i=0;i<colunms;i++){ drops.push(0); } //运动的文字 var str =\"WELCOME TO WWW.ITRHX.COM\"; //4:fillText(str,x,y);原理就是去更改y的坐标位置 //绘画的函数 function draw(){ context.fillStyle = \"rgba(238,238,238,.08)\";//遮盖层 context.fillRect(0,0,W,H); //给字体设置样式 context.font = \"600 \"+fontSize+\"px Georgia\"; //给字体添加颜色 context.fillStyle = [\"#33B5E5\", \"#0099CC\", \"#AA66CC\", \"#9933CC\", \"#99CC00\", \"#669900\", \"#FFBB33\", \"#FF8800\", \"#FF4444\", \"#CC0000\"][parseInt(Math.random() * 10)];//randColor();可以rgb,hsl, 标准色,十六进制颜色 //写入画布中 for(var i=0;i<colunms;i++){ var index = Math.floor(Math.random() * str.length); var x = i*fontSize; var y = drops[i] *fontSize; context.fillText(str[index],x,y); //如果要改变时间,肯定就是改变每次他的起点 if(y >= canvas.height && Math.random() > 0.99){ drops[i] = 0; } drops[i]++; } }; function randColor(){//随机颜色 var r = Math.floor(Math.random() * 256); var g = Math.floor(Math.random() * 256); var b = Math.floor(Math.random() * 256); return \"rgb(\"+r+\",\"+g+\",\"+b+\")\"; } draw(); setInterval(draw,35);}; 在主题文件的相关css文件中(以 Material X 1.2.1 主题为例,在\\themes\\material-x-1.2.1\\source\\less\\_main.less 文件末尾)添加以下代码: 12345678910canvas { position: fixed; right: 0px; bottom: 0px; min-width: 100%; min-height: 100%; height: auto; width: auto; z-index: -1;} 然后在主题的 layout.ejs 文件中引入即可: 123<!-- 数字雨 --><canvas id=\"canvas\" width=\"1440\" height=\"900\" ></canvas><script type=\"text/javascript\" src=\"/js/DigitalRain.js\"></script> 最终效果: 代码来源:http://www.lxl8800.cn/Main/Resource 【20】自定义一个不使用主题模板渲染的独立页面     有时候我们需要新建一个独立的页面,这个页面不使用主题的渲染,具有自己独立的样式,可以放一些自己的作品,相册什么的,以下就介绍这种独立页面的实现方法。 方法一:     使用 Hexo 提供的跳过渲染配置,在博客根目录的配置文件 _config.yml 里找到 skip_render 关键字,在后面添加想要跳过渲染的页面,比如我们创建 \\source\\about\\index.html, 配置文件填写:skip_render: about\\**,那么就表示 \\source\\about 里所有的文件将跳过渲染,里面的文件将会被直接复制到 public 文件夹,此时就会得到一个独立的 about 页面;官方文档:https://hexo.io/docs/configuration 方法二:     在文章头部的 Front-matter 里添加配置 layout: false 来跳过渲染配置,比如我们要使 about 页面跳过渲染,创建 \\source\\about\\index.md,将这个页面的相关 HTML 代码写进.md文件并保存,然后在 index.md 的头部写入: 123456789---layout: false---{% raw %}这里是 HTML 代码{% endraw %} PS:Front-matter 是 .md 文件最上方以 — 分隔的区域,用于指定个别文件的变量,官方文档:https://hexo.io/docs/front-matter 效果可以对比我的博客主页和关于页面 【21】更改本地预览端口号hexo博客在执行 hexo s 进行本地预览的时候,默认端口号是4000,当该端口号被占用时会报错 Error: listen EADDRINUSE 0.0.0.0:4000 ,此时可以关闭占用该端口的进程,也可以更换端口号,更换端口号可以通过以下两种方法实现: 方法一:在根目录的 _config.yml 配置文件内加上如下代码更改 hexo s 运行时的端口号: 1234server: port: 5000 compress: true header: true 方法二:通过 hexo server -p 5000 命令来指定端口,这种方法只是本次执行有效 未完待续……","categories":[{"name":"Hexo","slug":"Hexo","permalink":"https://www.itrhx.com/categories/Hexo/"}],"tags":[{"name":"主题个性化","slug":"主题个性化","permalink":"https://www.itrhx.com/tags/主题个性化/"},{"name":"Hexo","slug":"Hexo","permalink":"https://www.itrhx.com/tags/Hexo/"},{"name":"Material X","slug":"Material-X","permalink":"https://www.itrhx.com/tags/Material-X/"},{"name":"spfk","slug":"spfk","permalink":"https://www.itrhx.com/tags/spfk/"}]},{"title":"Markdown 语法&技巧总结","slug":"A03-markdown","date":"2018-08-25T09:57:16.879Z","updated":"2020-03-14T05:26:44.635Z","comments":true,"path":"2018/08/25/A03-markdown/","link":"","permalink":"https://www.itrhx.com/2018/08/25/A03-markdown/","excerpt":"","text":"在写博客的时候,我们不希望都是千篇一律的没有色彩,多了解一些 Markdown 语法技巧有利于丰富我们的博客,看起来更有 feel ! – 插入图片 如果你使用 MarkdownPad 的话就比较方便,可以直接选择插入本地图片或者是网络图片,实质是通过以下代码实现的,小括号里面就是你的图片地址,中括号里面是图片的替代文字,比如上面的图片代码如下:1![车](https://cdn.jsdelivr.net/gh/TRHX/ImageHosting/ITRHX-PIC/A03/01.jpg) – 插入音乐 打开网页版网易云音乐,选择你准备插入的音乐,点击生成外链播放器,前提是要有版权,不然是无法生成外链播放器的,选择好尺寸后,复制底下的HTML代码 然后将此HTML代码粘贴到你想要放的地方,可自行调节播放器的大小,其中 auto=1 表示打开网页自动播放音乐,auto=0 表示关闭自动播放音乐,比如See You Again (中英文版) - 罗艺恒这首歌曲代码如下: 1<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"//music.163.com/outchain/player?type=2&id=32405683&auto=1&height=66\"></iframe> – 插入视频 高考毕业了我们为下一届的学弟学妹们录制高考加油视频,我担任后期制作,在这里就以该视频为例٩(๑❛ᴗ❛๑)۶,在腾讯视频播放页面找到分享按钮,复制该视频的通用代码(其他视频播放平台也一样),粘贴到文章中对应位置即可,可根据情况调整视频播放器的大小 1<iframe frameborder=\"0\" width=\"840\" height=\"500\" src=\"https://v.qq.com/txp/iframe/player.html?vid=x0643zvgtf7\" allowFullScreen=\"true\"></iframe> 未完待续……","categories":[{"name":"Markdown","slug":"Markdown","permalink":"https://www.itrhx.com/categories/Markdown/"}],"tags":[{"name":"Markdown","slug":"Markdown","permalink":"https://www.itrhx.com/tags/Markdown/"},{"name":"技巧","slug":"技巧","permalink":"https://www.itrhx.com/tags/技巧/"}]},{"title":"使用 Github Pages 和 Hexo 搭建自己的独立博客","slug":"A02-hexo-blog","date":"2018-08-15T13:34:58.325Z","updated":"2020-04-26T04:01:45.694Z","comments":true,"path":"2018/08/15/A02-hexo-blog/","link":"","permalink":"https://www.itrhx.com/2018/08/15/A02-hexo-blog/","excerpt":"","text":"加入 Hexo 博客交流群:924812033,有问题互相交流学习! – 前言 首先感谢您能访问我的博客:TRHX’S BLOG 这是一篇有关如何使用 Github Pages 和 Hexo 搭建属于自己独立博客的详尽教程,本人是软件工程专业本科生,目前只学习了C和C++编程语言,对网站开发的有关知识几乎为零,这也是我搭建好自己的博客之后写的第一篇博客,刚开始搭建博客的时候自己也是网上各种百度,由于自己属于小白那种,历经了千辛万苦才弄好,所以借这个机会写一篇小白真正能看懂的博客搭建教程,教你一步一步走向成功的彼岸! 推荐文章: 《我为什么写博客》 (By 知明所以) 《为什么你应该(从现在开始就)写博客》 (By 刘未鹏 | Mind Hacks) – 入门 Github Pages Github Pages可以被认为是用户编写的、托管在github上的静态网页。使用Github Pages可以为你提供一个免费的服务器,免去了自己搭建服务器和写数据库的麻烦。此外还可以绑定自己的域名。 Hexo Hexo 是一个快速、简洁且高效的博客框架。Hexo 使用 Markdown(或其他渲染引擎)解析文章,在几秒内,即可利用靓丽的主题生成静态网页。 – 安装 Node.js点击此处访问官网,按需下载相应版本,默认安装可以了 注:本人在安装过程中出现了Warning 1909,无法创建快捷方式,这种情况很少出现,如果在安装过程中也有这种情况请参考百度文库(win10系统实测可行):《Win7安装程序警告1909无法创建快捷方式》 – 安装 Git点击此处访问官网,按需下载相应版本,默认安装即可参考资料:《如何在windows下安装GIT》 (By 俊雨廷休) 《Pro Git(中文版)》 – 检验软件是否安装成功同时按下 Win 键和 R 键打开运行窗口,输入 cmd ,然后输入以下命令,有相应版本信息显示则安装成功,若不正确可以卸载软件重新安装,此外若安装成功,在桌面右键鼠标,可以看到菜单里多了 Git GUI Here 和 Git Bash Here两个选项,第一个是图形界面的Git操作,另一个是命令行123$ git --version$ node -v$ npm -v – Hexo 安装选择一个磁盘,新建一个文件夹,自己重命名文件夹(如:我的文件夹为:E\\TRHX_Blog),博客相关文件将储存在此文件夹下,在该文件夹下右键鼠标,点击 Git Bash Here,输入以下 npm 命令即可安装,第一个命令表示安装 hexo,第二个命令表示安装 hexo 部署到 git page 的 deployer,如图所示即为安装成功12$ npm install hexo-cli -g$ npm install hexo-deployer-git --save – Hexo 初始化配置在刚才新建的文件夹里面再次新建一个 Hexo 文件夹(如:我的文件夹为:E\\TRHX_Blog\\Hexo),进入该 Hexo 文件夹右键鼠标,点击 Git Bash Here,输入以下命令,如图所示则安装成功1$ hexo init Hexo 安装完成后,将会在指定文件夹中新建所需要的文件,Hexo 文件夹下的目录如下: – 本地查看效果执行以下命令,执行完即可登录 http://localhost:4000/ 查看效果12$ hexo generate$ hexo server 显示以下信息说明操作成功:1INFO Hexo is running at http://0.0.0.0:4000/. Press Ctrl+C to stop. 登录 http://localhost:4000/ 查看效果: – 将博客部署到 Github Pages 上到目前为止,我们的本地博客就成功搭建了,但是现在我们只能通过本地连接查看博客,我们要做的是让其他人也能够访问我们的博客,这就需要我们将博客部署到Github Pages上 一、注册 Github 账户:点击此处访问 Github 官网,点击 Sign Up 注册账户 二、创建项目代码库:点击 New repository 开始创建,步骤及注意事项见图: 三、配置 SSH 密钥:只有配置好 SSH 密钥后,我们才可以通过 git 操作实现本地代码库与 Github 代码库同步,在你第一次新建的文件夹里面(如:我的文件夹为:E\\TRHX_Blog) Git Bash Here 输入以下命令:12$ ssh-keygen -t rsa -C "your email@example.com"//引号里面填写你的邮箱地址,比如我的是tanrenhou@126.com 之后会出现:123Generating public/private rsa key pair.Enter file in which to save the key (/c/Users/you/.ssh/id_rsa)://到这里可以直接回车将密钥按默认文件进行存储 然后会出现:123Enter passphrase (empty for no passphrase)://这里是要你输入密码,其实不需要输什么密码,直接回车就行Enter same passphrase again: 接下来屏幕会显示:123456Your identification has been saved in /c/Users/you/.ssh/id_rsa.Your public key has been saved in /c/Users/you/.ssh/id_rsa.pub.The key fingerprint is:这里是各种字母数字组成的字符串,结尾是你的邮箱The key's randomart image is:这里也是各种字母数字符号组成的字符串 运行以下命令,将公钥的内容复制到系统粘贴板上1$ clip < ~/.ssh/id_rsa.pub 四、在 GitHub 账户中添加你的公钥 1.登陆 GitHub,进入 Settings: 2.点击 SSH and GPG Keys: 3.选择 New SSH key: 4.粘贴密钥: 五、测试 输入以下命令:注意:git@github.com不要做任何更改!1$ ssh -T git@github.com 之后会显示: 输入 yes 后会显示:此时表示设置正确 六、配置 Git 个人信息 Git 会根据用户的名字和邮箱来记录提交,GitHub 也是用这些信息来做权限的处理,输入以下命令进行个人信息的设置,把名称和邮箱替换成你自己的,名字可以不是 GitHub 的昵称,但为了方便记忆,建议与 GitHub 一致12$ git config --global user.name "此处填你的用户名"$ git config --global user.email "此处填你的邮箱" 到此为止 SSH Key 配置成功,本机已成功连接到 Github – 将本地的 Hexo 文件更新到 Github 的库中一、登录 Github 打开自己的项目 yourname.github.io 二、鼠标移到 Clone or download 按钮,选择 Use SSH 三、一键复制地址 四、打开你创建的 Hexo 文件夹(如:E:\\TRHX_Blog\\Hexo),右键用记事本(或者Notepad++、Vs Code等)打开该文件夹下的 _config.yml 文件 五、按下图修改 _config.yml 文件并保存 六、在 Hexo 文件夹下分别执行以下命令12$ hexo g$ hexo d 或者直接执行1$ hexo g -d 执行完之后会让你输入你的 Github 的账号和密码,如果此时报以下错误,说明你的 deployer 没有安装成功1ERROR Deployer not found: git 需要执行以下命令再安装一次:1npm install hexo-deployer-git --save 再执行 hexo g -d,你的博客就会部署到 Github 上了 七、访问博客 你的博客地址:https://你的用户名.github.io,比如我的是:https://trhx.github.io ,现在每个人都可以通过此链接访问你的博客了 – 如何在博客上发表文章博客已经成功搭建了,但是我们该怎么写博客呢? 一、新建一个空文章,输入以下命令,会在项目 \\Hexo\\source\\_posts 中生成 文章标题.md 文件,文章标题根据需要命名1$ hexo n "文章标题" 也可以直接在 \\Hexo\\source\\_posts 目录下右键鼠标新建文本文档,改后缀为 .md 即可,这种方法比较方便 二、用编辑器编写文章 md 全称 Markdown, Markdown 是 2004 年由 John Gruberis 设计和开发的纯文本格式的语法,非常的简单实用,常用的标记符号屈指可数,几分钟即可学会, .md 文件可以使用支持 Markdown 语法的编辑器编辑,然后将写好的文章(.md文件)保存到 \\Hexo\\source\\_posts 文件夹下即可推荐 Windows 上使用 MarkdownPad2 或者 小书匠 编辑器,macOS 上使用 Mou 编辑器,Linux 上使用 Remarkable 编辑器,Web 端上使用 简书 ,另外可以参考我的另一篇文章:《主流 Markdown 编辑器推荐》当我们用编辑器写好文章后,可以使用以下命令将其推送到服务器上12$ hexo g$ hexo d或者将两个命令合二为一输入以下命令:1$ hexo d -g现在访问你的博客就可以看见写好的文章啦!参考资料:《10款流行的Markdown编辑器》 (By xiaoxiao_engineer) 《献给写作者的 Markdown 新手指南》 (By 简书) 《认识与入门 Markdown》 (By Te_Lee) 《markdown简明语法》 (By 不如) 《markdown基本语法》 (By 高鸿祥) 《Markdown 公式指导手册》 (By Harries)# – 如何为博客更换自己喜欢的主题 博客也搭建好了,文章也会写了,但是!!!默认的主题并不喜欢怎么办?现在,我们就来为自己的博客更换自己喜欢的主题 点击此处进入 Hexo 官网的主题专栏,我们可以看见有许多的主题供我们选择 我们要做的就是把主题克隆过来,在此我们以主题 Aero-Dual 为例,点进去我们就可以看见该主题作者的博客,鼠标滑到底,我们可以看见 Theme By Levblanc 的字样(其他主题类似),点击作者 Levblanc ,页面就会跳转到该主题所有的相关文件在 Github 上的地址,复制该地址 再打开 Hexo 文件夹下的 themes 目录(如:E:\\TRHX_Blog\\Hexo\\themes),右键 Git Bash Here,输入以下命令:1$ git clone 此处填写你刚才复制的主题地址 比如要安装 Aero-Dual 主题,则输入命令:1$ git clone https://github.com/levblanc/hexo-theme-aero-dual 等待下载完成后即可在 themes 目录下生成 hexo-theme-aero-dual 文件夹,然后打开 Hexo 文件夹下的配置文件 _config.yml ,找到关键字 theme,修改参数为:theme:hexo-theme-aero-dual (其他主题修改成相应名称即可),再次注意冒号后面有一个空格! 返回 Hexo 目录,右键 Git Bash Here ,输入以下命令开始部署主题:12$ hexo g $ hexo s 此时打开浏览器,访问 http://localhost:4000/ 就可看见我们的主题已经更换了,如果感觉效果满意,我们就可以把它部署到Github上了 打开 Hexo 文件夹,右键 Git Bash Here ,输入以下命令:123$ hexo clean //该命令的作用是清除缓存,若不输入此命令,服务器有可能更新不了主题$ hexo g -d 此时访问自己的博客即可看见更换后的主题,但我们仍然需要对主题的相关配置进行修改,比如网站标题,图标等等,Hexo 中有两份主要的配置文件,名称都是 _config.yml ,它们均是用于站点配置使用的。其中,一份位于站点根目录下(比如我的:E:\\TRHX_Blog\\Hexo\\_config.yml),主要包含 Hexo 本身整站的配置;另一份位于主题目录下(比如我的:E:\\TRHX_Blog\\Hexo\\themes\\hexo-theme-aero-dual\\_config.yml),这份配置由主题作者提供,主要用于配置主题相关的选项,一般 _config.yml 文件里都有相关注释,按需修改即可 参考资料:《有哪些好看的 Hexo 主题?》 (知乎) 《Hexo | 配置》 (Hexo官方文档) 《hexo常用命令笔记》 (By 小弟调调) – 为你的 Hexo 博客配置个性域名本人在配置域名的时候问题百出,百度的各种方法都不管用,打开网站总是 404,可能是我太笨了 o(╥﹏╥)o ,不过好在后来终于解决了这个问题 首先我们要购买域名,阿里云,腾讯云都可以,也不贵,一年几十块钱,最便宜几块钱也能买到,以阿里云为例,我购买的域名是 itrhx.com,购买过程就不赘述了,选择阿里云的解析平台,来到阿里云的管理控制台,点击进入域名解析列表或者直接点击域名后面的解析 方法一:点击添加记录,需要添加两个记录,两个记录类型都是 CNAME ,第一个主机记录为 @ ,第二个主机记录为 www,记录值都是填你自己的博客地址(比如我的是:trhx.github.io),保存之后域名解析就完成了!方法二:两个记录类型为 A ,第一个主机记录为 @ ,第二个主机记录为 www,记录值都为博客的 IP 地址,IP 地址可以 cmd 中输入 ping 你的博客地址 获得(比如我的:ping trhx.github.io),保存之后域名解析就完成了!有关解析记录类型的区别可以参考《域名解析中A记录、CNAME、MX记录、NS记录的区别和联系》 为了使 GitHub 接收我们的域名,还需要在博客的根目录下添加一个名为 CNAME 的文件(注意不要加.txt,没有任何后缀名!),这个文件放到 Hexo 文件夹的 source 里面,(比如我的是:E:\\TRHX_Blog\\Hexo\\source),文件里面填写你的域名(加不加www都行),比如要填写我的域名,文件里面就写:www.itrhx.com 或者 itrhx.com,经过以上操作,别人就可以通过 www.itrhx.com 、itrhx.com 、trhx.github.io 三个当中任意一个访问我的博客了!你的也一样! 有关加不加www的问题有以下区别: 如果你填写的是没有www的,比如 itrhx.com,那么无论是访问 https://www.itrhx.com 还是 https://itrhx.com ,都会自动跳转到 https://itrhx.com 如果你填写的是带www的,比如 www.itrhx.com ,那么无论是访问 https://www.itrhx.com 还是 https://itrhx.com ,都会自动跳转到 http://www.itrhx.com 如果你在其他平台购买域名,或者选择 DNSPod 等其他域名解析,操作方法大同小异,遇到问题可自行百度解决! 参考资料:《推荐几家域名注册服务商》 (By Jelly Bool) 《盘点十大免费DNS域名解析服务:稳定、可靠》 – 结语一顿操作下来虽然有点儿累,但看见拥有了自己的博客还是非常有成就感的,人生就是需要折腾,那么现在就开始你的创作之旅吧!文章的不断积累,你会从中受益很多的!另外,这是一篇小白写的适用于小白的博客搭建教程,比较详细,有这方面基础的可以百度有简略一点儿的教程,文中如有错误还请大佬指出改正!文中涉及参考资料如有侵权请联系我删除!","categories":[{"name":"Hexo","slug":"Hexo","permalink":"https://www.itrhx.com/categories/Hexo/"}],"tags":[{"name":"Hexo","slug":"Hexo","permalink":"https://www.itrhx.com/tags/Hexo/"},{"name":"Github Pages","slug":"Github-Pages","permalink":"https://www.itrhx.com/tags/Github-Pages/"}]},{"title":"Hello World!","slug":"A01-hello-world","date":"2018-08-10T09:38:00.000Z","updated":"2019-09-09T14:13:26.239Z","comments":true,"path":"2018/08/10/A01-hello-world/","link":"","permalink":"https://www.itrhx.com/2018/08/10/A01-hello-world/","excerpt":"","text":"人类的幸福和欢乐在于奋斗,而最有价值的是为理想而奋斗! ——— 苏格拉底 Human happiness and joy lie in struggle, and what is most valuable is striving for ideals! ——— Socrates","categories":[{"name":"BLOG","slug":"BLOG","permalink":"https://www.itrhx.com/categories/BLOG/"}],"tags":[{"name":"BLOG","slug":"BLOG","permalink":"https://www.itrhx.com/tags/BLOG/"}]}]} \ No newline at end of file +{"meta":{"title":"TRHX'S BLOG","subtitle":"一入 IT 深似海 从此学习无绝期","description":"TRHX 的个人博客;主攻 Python、爬虫、WEB前端、大数据、数据分析、数据可视化;求知若饥,虚心若愚,一入 IT 深似海,从此学习无绝期,记录毕生所学!","author":"TRHX","url":"https://www.itrhx.com"},"pages":[{"title":"","date":"2019-09-23T02:54:05.384Z","updated":"2019-09-23T02:54:05.384Z","comments":true,"path":"404.html","permalink":"https://www.itrhx.com/404.html","excerpt":"","text":"404 页面 | TRHX'S BLOG *{padding:0;margin:0}body{background-color:#eee;background-image:url(\"https://cdn.jsdelivr.net/gh/TRHX/CDN-for-itrhx.com@2.1.9/images/background/021.webp\");}#catch-the-cat{width:100%;margin-top:32px;text-align:center}#Copyright{text-align:center;font-size:15px;color:#000;margin:30px}#Copyright a{color:#000}#Copyright a:hover{color:#f00} 404 Not Found! 很抱歉,您访问的页面不存在,可能是输入地址有误或该地址已被删除! window.game = new CatchTheCatGame({ w: 11, h: 11, r: 20, backgroundColor: 0xffffff, parent: 'catch-the-cat', statusBarAlign: 'center', credit: 'www.itrhx.com' }); Copyright ©2018-2019 TRHX'S BLOG       鄂ICP备19003281号-3     点击返回首页"},{"title":"","date":"2020-02-28T07:25:47.215Z","updated":"2020-02-28T07:25:47.215Z","comments":true,"path":"2019-nCoV/index.html","permalink":"https://www.itrhx.com/2019-nCoV/index.html","excerpt":"","text":"全国新型冠状病毒实时分布图 body { height: 88vh; margin: 0; padding: 0; overflow: hidden; } h2 { text-align:center; margin: 20px 0 0 0; color: #0822B5; } h3 { text-align:center; margin: 20px 0 0 0; color: #0822B5; } a { color: #178b50; text-decoration: none; } a:hover { color: #d81d1b; text-decoration: none; } iframe { overflow:hidden; margin: 20px 0 0 0; border: none; } TRHX'S BLOG丨新冠肺炎实时疫情图(数据来源:新浪新闻) 相关链接:武汉新型冠状病毒防疫信息收集平台 丨 2019-nCoV 疫情信息导航网站"},{"title":"","date":"2020-03-02T05:42:14.094Z","updated":"2020-03-02T05:42:14.094Z","comments":true,"path":"about/index.html","permalink":"https://www.itrhx.com/about/index.html","excerpt":"","text":"TRHX'S BLOG | ABOUT document.onkeydown = function () { if (window.event && window.event.keyCode == 123) { event.keyCode = 0; event.returnValue = false; return false; } }; 您的浏览器不支持audio标签,无法播放音乐! 江湖名称:TRHX 常驻之地:China~丨湖北丨武汉 初度之辰:1999 擅长领域:Python丨爬虫丨前端 技能丨Skill HTML/CSS/JS 65% C/C++ 20% JAVA 30% PYTHON 80% HEXO/GIT 90% PS/PR/AE 65% 简介丨Introduction ● 学历:武汉某本科,软件工程专业大三学生; ● 现况:自学 Python 中,网络爬虫方向; ● 目标:优秀前端工程师 or 网络爬虫工程师; ● 博客:好记性不如烂笔头,记录学习过程; ● 兴趣:酷爱编程,业余公路自行车手,WOT 玩家; ● 其他:虽然很菜,但是在努力学习中! 联系我丨Contact me Copyright © 2018-2020 TRHX'S BLOG. All rights reserved. if ('addEventListener' in window) { window.addEventListener('load', function () { document.body.className = document.body.className.replace(/\\bis-loading\\b/, ''); }); document.body.className += (navigator.userAgent.match(/(MSIE|rv:11\\.0)/) ? ' is-ie' : ''); } uniform mat4 uProjection; uniform mat4 uModelview; uniform vec3 uResolution; uniform vec3 uOffset; uniform vec3 uDOF; //x:focus distance, y:focus radius, z:max radius uniform vec3 uFade; //x:start distance, y:half distance, z:near fade start attribute vec3 aPosition; attribute vec3 aEuler; attribute vec2 aMisc; //x:size, y:fade varying vec3 pposition; varying float psize; varying float palpha; varying float pdist; //varying mat3 rotMat; varying vec3 normX; varying vec3 normY; varying vec3 normZ; varying vec3 normal; varying float diffuse; varying float specular; varying float rstop; varying float distancefade; void main(void) { // Projection is based on vertical angle vec4 pos = uModelview * vec4(aPosition + uOffset, 1.0); gl_Position = uProjection * pos; gl_PointSize = aMisc.x * uProjection[1][1] / -pos.z * uResolution.y * 0.5; pposition = pos.xyz; psize = aMisc.x; pdist = length(pos.xyz); palpha = smoothstep(0.0, 1.0, (pdist - 0.1) / uFade.z); vec3 elrsn = sin(aEuler); vec3 elrcs = cos(aEuler); mat3 rotx = mat3( 1.0, 0.0, 0.0, 0.0, elrcs.x, elrsn.x, 0.0, -elrsn.x, elrcs.x ); mat3 roty = mat3( elrcs.y, 0.0, -elrsn.y, 0.0, 1.0, 0.0, elrsn.y, 0.0, elrcs.y ); mat3 rotz = mat3( elrcs.z, elrsn.z, 0.0, -elrsn.z, elrcs.z, 0.0, 0.0, 0.0, 1.0 ); mat3 rotmat = rotx * roty * rotz; normal = rotmat[2]; mat3 trrotm = mat3( rotmat[0][0], rotmat[1][0], rotmat[2][0], rotmat[0][1], rotmat[1][1], rotmat[2][1], rotmat[0][2], rotmat[1][2], rotmat[2][2] ); normX = trrotm[0]; normY = trrotm[1]; normZ = trrotm[2]; const vec3 lit = vec3(0.6917144638660746, 0.6917144638660746, -0.20751433915982237); float tmpdfs = dot(lit, normal); if(tmpdfs < 0.0) { normal = -normal; tmpdfs = dot(lit, normal); } diffuse = 0.4 + tmpdfs; vec3 eyev = normalize(-pos.xyz); if(dot(eyev, normal) > 0.0) { vec3 hv = normalize(eyev + lit); specular = pow(max(dot(hv, normal), 0.0), 20.0); } else { specular = 0.0; } rstop = clamp((abs(pdist - uDOF.x) - uDOF.y) / uDOF.z, 0.0, 1.0); rstop = pow(rstop, 0.5); //-0.69315 = ln(0.5) distancefade = min(1.0, exp((uFade.x - pdist) * 0.69315 / uFade.y)); } #ifdef GL_ES //precision mediump float; precision highp float; #endif uniform vec3 uDOF; //x:focus distance, y:focus radius, z:max radius uniform vec3 uFade; //x:start distance, y:half distance, z:near fade start const vec3 fadeCol = vec3(0.08, 0.03, 0.06); varying vec3 pposition; varying float psize; varying float palpha; varying float pdist; //varying mat3 rotMat; varying vec3 normX; varying vec3 normY; varying vec3 normZ; varying vec3 normal; varying float diffuse; varying float specular; varying float rstop; varying float distancefade; float ellipse(vec2 p, vec2 o, vec2 r) { vec2 lp = (p - o) / r; return length(lp) - 1.0; } void main(void) { vec3 p = vec3(gl_PointCoord - vec2(0.5, 0.5), 0.0) * 2.0; vec3 d = vec3(0.0, 0.0, -1.0); float nd = normZ.z; //dot(-normZ, d); if(abs(nd) < 0.0001) discard; float np = dot(normZ, p); vec3 tp = p + d * np / nd; vec2 coord = vec2(dot(normX, tp), dot(normY, tp)); //angle = 15 degree const float flwrsn = 0.258819045102521; const float flwrcs = 0.965925826289068; mat2 flwrm = mat2(flwrcs, -flwrsn, flwrsn, flwrcs); vec2 flwrp = vec2(abs(coord.x), coord.y) * flwrm; float r; if(flwrp.x < 0.0) { r = ellipse(flwrp, vec2(0.065, 0.024) * 0.5, vec2(0.36, 0.96) * 0.5); } else { r = ellipse(flwrp, vec2(0.065, 0.024) * 0.5, vec2(0.58, 0.96) * 0.5); } if(r > rstop) discard; vec3 col = mix(vec3(1.0, 0.8, 0.75), vec3(1.0, 0.9, 0.87), r); float grady = mix(0.0, 1.0, pow(coord.y * 0.5 + 0.5, 0.35)); col *= vec3(1.0, grady, grady); col *= mix(0.8, 1.0, pow(abs(coord.x), 0.3)); col = col * diffuse + specular; col = mix(fadeCol, col, distancefade); float alpha = (rstop > 0.001)? (0.5 - r / (rstop * 2.0)) : 1.0; alpha = smoothstep(0.0, 1.0, alpha) * palpha; gl_FragColor = vec4(col * 0.5, alpha); } uniform vec3 uResolution; attribute vec2 aPosition; varying vec2 texCoord; varying vec2 screenCoord; void main(void) { gl_Position = vec4(aPosition, 0.0, 1.0); texCoord = aPosition.xy * 0.5 + vec2(0.5, 0.5); screenCoord = aPosition.xy * vec2(uResolution.z, 1.0); } #ifdef GL_ES //precision mediump float; precision highp float; #endif uniform vec2 uTimes; varying vec2 texCoord; varying vec2 screenCoord; void main(void) { vec3 col; float c; vec2 tmpv = texCoord * vec2(0.8, 1.0) - vec2(0.95, 1.0); c = exp(-pow(length(tmpv) * 1.8, 2.0)); col = mix(vec3(0.02, 0.0, 0.03), vec3(0.96, 0.98, 1.0) * 1.5, c); gl_FragColor = vec4(col * 0.5, 1.0); } #ifdef GL_ES //precision mediump float; precision highp float; #endif uniform sampler2D uSrc; uniform vec2 uDelta; varying vec2 texCoord; varying vec2 screenCoord; void main(void) { vec4 col = texture2D(uSrc, texCoord); gl_FragColor = vec4(col.rgb * 2.0 - vec3(0.5), 1.0); } #ifdef GL_ES //precision mediump float; precision highp float; #endif uniform sampler2D uSrc; uniform vec2 uDelta; uniform vec4 uBlurDir; //dir(x, y), stride(z, w) varying vec2 texCoord; varying vec2 screenCoord; void main(void) { vec4 col = texture2D(uSrc, texCoord); col = col + texture2D(uSrc, texCoord + uBlurDir.xy * uDelta); col = col + texture2D(uSrc, texCoord - uBlurDir.xy * uDelta); col = col + texture2D(uSrc, texCoord + (uBlurDir.xy + uBlurDir.zw) * uDelta); col = col + texture2D(uSrc, texCoord - (uBlurDir.xy + uBlurDir.zw) * uDelta); gl_FragColor = col / 5.0; } #ifdef GL_ES //precision mediump float; precision highp float; #endif uniform sampler2D uSrc; uniform vec2 uDelta; varying vec2 texCoord; varying vec2 screenCoord; void main(void) { gl_FragColor = texture2D(uSrc, texCoord); } uniform vec3 uResolution; attribute vec2 aPosition; varying vec2 texCoord; varying vec2 screenCoord; void main(void) { gl_Position = vec4(aPosition, 0.0, 1.0); texCoord = aPosition.xy * 0.5 + vec2(0.5, 0.5); screenCoord = aPosition.xy * vec2(uResolution.z, 1.0); } #ifdef GL_ES //precision mediump float; precision highp float; #endif uniform sampler2D uSrc; uniform sampler2D uBloom; uniform vec2 uDelta; varying vec2 texCoord; varying vec2 screenCoord; void main(void) { vec4 srccol = texture2D(uSrc, texCoord) * 2.0; vec4 bloomcol = texture2D(uBloom, texCoord); vec4 col; col = srccol + bloomcol * (vec4(1.0) + srccol); col *= smoothstep(1.0, 0.0, pow(length((texCoord - vec2(0.5)) * 2.0), 1.2) * 0.5); col = pow(col, vec4(0.45454545454545)); //(1.0 / 2.2) gl_FragColor = vec4(col.rgb, 1.0); gl_FragColor.a = 1.0; }"},{"title":"留言","date":"2020-04-16T04:39:29.101Z","updated":"2020-04-16T04:39:29.101Z","comments":true,"path":"comments/index.html","permalink":"https://www.itrhx.com/comments/index.html","excerpt":"","text":"                                    采用 Gitalk 评论系统,需使用 GitHub 账号登录,请尽情灌水吧!😉由于 Gitalk 调用的是 GitHub 的 issues,如果您参与了评论,可能别人评论您也会收到邮件,若不想再收到邮件,可以到 issues 页面取消订阅,取消后,如果您再次评论,也不会再收到邮件提醒!"},{"title":"所有标签","date":"2019-05-05T16:01:26.324Z","updated":"2019-05-05T16:01:26.324Z","comments":true,"path":"tags/index.html","permalink":"https://www.itrhx.com/tags/index.html","excerpt":"","text":""},{"title":"所有分类","date":"2019-04-27T16:28:11.064Z","updated":"2019-04-27T16:28:11.064Z","comments":true,"path":"categories/index.html","permalink":"https://www.itrhx.com/categories/index.html","excerpt":"","text":""},{"title":"小伙伴们","date":"2020-08-18T01:45:12.586Z","updated":"2020-08-18T01:45:12.586Z","comments":false,"path":"friends/index.html","permalink":"https://www.itrhx.com/friends/index.html","excerpt":"","text":"名称:TRHX’S BLOG主页:https://www.itrhx.com/头像:https://www.itrhx.com/images/trhx.png标签:Python、爬虫、前端简介:求知若饥,虚心若愚! 由于目前友链数过多,所以暂停交换友链,敬请见谅!长期不能访问的站点将会被删除,若已恢复请留言告知!"},{"title":"","date":"2019-12-29T06:55:50.751Z","updated":"2019-12-29T06:55:50.751Z","comments":true,"path":"box/about/index.html","permalink":"https://www.itrhx.com/box/about/index.html","excerpt":"","text":"关于本页丨TRHX'S BLOG 关于本页 本页面收集了比较常用或者实用的网站,相当于一个小小的导航页面。 整个页面由 Viggo 开发,完全开源,如果你也喜欢,欢迎去其 Github 点亮 star。 关于 Viggo Designer. Viggo. Full-time UI designer with an enduring interest in Coding. 一个全职的用户界面设计师,优秀的前端开发工程师,擅长 WEB 开发、WEB 设计、UI/UX 设计,对编程,拍照和单车有着持久的兴趣,生活在广州;如果您想招收此方面的人才,Viggo 无疑是一个很好的选择。 关于 TRHX TRHX 在校本科软件工程大三学生,主攻 Python、爬虫和前端。 一个想成为大佬的在校本科生,倾向于 Python、网络爬虫、数据分析、数据可视化、WEB 前端方面的学习,热爱编程、单车和户外运动,坚信好记性不如烂笔头,业精于勤荒于嬉,行成于思毁于随。 COPYRIGHT 2018 - 2020 WEBSTACK 丨 DESIGNED BY VIGGO 丨 CHANGED BY TRHX"},{"title":"","date":"2019-12-29T06:55:50.753Z","updated":"2019-12-29T06:55:50.753Z","comments":true,"path":"games/PacMan/index.html","permalink":"https://www.itrhx.com/games/PacMan/index.html","excerpt":"","text":"吃豆人 | TRHX'S BLOG body{background-color: #000} *{padding:0;margin:0;} .wrapper{ width: 960px; margin:0 auto; line-height:36px; text-align:center; color:#999; } canvas{display:block;background: #000;} .mod-botton{ height: 32px; padding: 15px 0; text-align: center; } #footer{position:relative;clear:both;padding:10px 20px 40px 0;padding:10px 0;width:100%;text-align:center}#footer address{display:inline-block;padding:2px 10px;color:rgba(255, 255, 255, 0.5);font-style:normal} #footer a{color:rgba(255, 255, 255, 0.5);cursor:grab}#footer a:hover{border-bottom:1px dotted #00387d;color:#00387d} 不支持画布 【按空格键开始、暂停或继续游戏,方向键移动吃豆人】 Copyright © 2018-2019 TRHX'S BLOG |    鄂ICP备19003281号-3 |  中国互联网违法和不良信息举报中心 推荐使用1920*1080分辨率、谷歌或者火狐浏览器浏览此页 |  站点地图 |   |  RSS订阅 |  996.ICU |  申请友链 |  Powered By Hexo |  Hosted By GitHub Pages 部分资料来源于网络,版权属于其合法持有人,只供学习交流之用,非商务用途。如有侵犯您的权益,请及时告知删除。互动交流时请遵守理性,宽容,换位思考的原则。 document.onkeydown=function (e){ var currKey=0,evt=e||window.event; currKey=evt.keyCode||evt.which||evt.charCode; if (currKey == 123) { window.event.cancelBubble = true; window.event.returnValue = false; } }"},{"title":"","date":"2019-12-29T06:55:50.751Z","updated":"2019-12-29T06:55:50.751Z","comments":true,"path":"games/element/index.html","permalink":"https://www.itrhx.com/games/element/index.html","excerpt":"","text":"3D元素周期表 | TRHX'S BLOG html, body { height: 100%; } body { background-color: #000000; margin: 0; font-family: Helvetica, sans-serif;; overflow: hidden; } a { color: #ffffff; } #info { position: absolute; width: 100%; color: #ffffff; padding: 5px; font-family: Monospace; font-size: 13px; font-weight: bold; text-align: center; z-index: 1; } #menu { position: absolute; bottom: 20px; width: 100%; text-align: center; font-family: verdana,Tahoma,Arial,Hei,\"Microsoft Yahei\",SimHei; } .element { width: 120px; height: 160px; box-shadow: 0px 0px 12px rgba(0,255,255,0.5); border: 1px solid rgba(127,255,255,0.25); text-align: center; cursor: default; } .element:hover { box-shadow: 0px 0px 12px rgba(0,255,255,0.75); border: 1px solid rgba(127,255,255,0.75); } .element .number { position: absolute; top: 20px; right: 20px; font-size: 12px; color: rgba(127,255,255,0.75); } .element .symbol { position: absolute; top: 40px; left: 0px; right: 0px; font-size: 60px; font-weight: bold; color: rgba(255,255,255,0.75); text-shadow: 0 0 10px rgba(0,255,255,0.95); } .element .details { position: absolute; bottom: 15px; left: 0px; right: 0px; font-size: 12px; color: rgba(127,255,255,0.75); } button { color: rgba(127,255,255,0.75); background: transparent; outline: 1px solid rgba(127,255,255,0.75); border: 0px; padding: 5px 10px; cursor: pointer; } button:hover { background-color: rgba(0,255,255,0.5); } button:active { color: #000000; background-color: rgba(0,255,255,0.75); } 表面 球体 螺旋 网格 var table = [ \"H\", \"Hydrogen\", \"1.00794\", 1, 1, \"He\", \"Helium\", \"4.002602\", 18, 1, \"Li\", \"Lithium\", \"#6.941\", 1, 2, \"Be\", \"Beryllium\", \"9.012182\", 2, 2, \"B\", \"Boron\", \"#10.811\", 13, 2, \"C\", \"Carbon\", \"#12.0107\", 14, 2, \"N\", \"Nitrogen\", \"#14.0067\", 15, 2, \"O\", \"Oxygen\", \"#15.9994\", 16, 2, \"F\", \"Fluorine\", \"18.9984032\", 17, 2, \"Ne\", \"Neon\", \"#20.1797\", 18, 2, \"Na\", \"Sodium\", \"22.98976...\", 1, 3, \"Mg\", \"Magnesium\", \"#24.305\", 2, 3, \"Al\", \"Aluminium\", \"26.9815386\", 13, 3, \"Si\", \"Silicon\", \"#28.0855\", 14, 3, \"P\", \"Phosphorus\", \"30.973762\", 15, 3, \"S\", \"Sulfur\", \"#32.065\", 16, 3, \"Cl\", \"Chlorine\", \"#35.453\", 17, 3, \"Ar\", \"Argon\", \"#39.948\", 18, 3, \"K\", \"Potassium\", \"#39.948\", 1, 4, \"Ca\", \"Calcium\", \"#40.078\", 2, 4, \"Sc\", \"Scandium\", \"44.955912\", 3, 4, \"Ti\", \"Titanium\", \"#47.867\", 4, 4, \"V\", \"Vanadium\", \"#50.9415\", 5, 4, \"Cr\", \"Chromium\", \"#51.9961\", 6, 4, \"Mn\", \"Manganese\", \"54.938045\", 7, 4, \"Fe\", \"Iron\", \"#55.845\", 8, 4, \"Co\", \"Cobalt\", \"58.933195\", 9, 4, \"Ni\", \"Nickel\", \"#58.6934\", 10, 4, \"Cu\", \"Copper\", \"#63.546\", 11, 4, \"Zn\", \"Zinc\", \"#65.38\", 12, 4, \"Ga\", \"Gallium\", \"#69.723\", 13, 4, \"Ge\", \"Germanium\", \"#72.63\", 14, 4, \"As\", \"Arsenic\", \"#74.9216\", 15, 4, \"Se\", \"Selenium\", \"#78.96\", 16, 4, \"Br\", \"Bromine\", \"#79.904\", 17, 4, \"Kr\", \"Krypton\", \"#83.798\", 18, 4, \"Rb\", \"Rubidium\", \"#85.4678\", 1, 5, \"Sr\", \"Strontium\", \"#87.62\", 2, 5, \"Y\", \"Yttrium\", \"88.90585\", 3, 5, \"Zr\", \"Zirconium\", \"#91.224\", 4, 5, \"Nb\", \"Niobium\", \"92.90628\", 5, 5, \"Mo\", \"Molybdenum\", \"#95.96\", 6, 5, \"Tc\", \"Technetium\", \"(98)\", 7, 5, \"Ru\", \"Ruthenium\", \"#101.07\", 8, 5, \"Rh\", \"Rhodium\", \"#102.9055\", 9, 5, \"Pd\", \"Palladium\", \"#106.42\", 10, 5, \"Ag\", \"Silver\", \"#107.8682\", 11, 5, \"Cd\", \"Cadmium\", \"#112.411\", 12, 5, \"In\", \"Indium\", \"#114.818\", 13, 5, \"Sn\", \"Tin\", \"#118.71\", 14, 5, \"Sb\", \"Antimony\", \"#121.76\", 15, 5, \"Te\", \"Tellurium\", \"127.6\", 16, 5, \"I\", \"Iodine\", \"126.90447\", 17, 5, \"Xe\", \"Xenon\", \"#131.293\", 18, 5, \"Cs\", \"Caesium\", \"#132.9054\", 1, 6, \"Ba\", \"Barium\", \"#132.9054\", 2, 6, \"La\", \"Lanthanum\", \"138.90547\", 4, 9, \"Ce\", \"Cerium\", \"#140.116\", 5, 9, \"Pr\", \"Praseodymium\", \"140.90765\", 6, 9, \"Nd\", \"Neodymium\", \"#144.242\", 7, 9, \"Pm\", \"Promethium\", \"(145)\", 8, 9, \"Sm\", \"Samarium\", \"#150.36\", 9, 9, \"Eu\", \"Europium\", \"#151.964\", 10, 9, \"Gd\", \"Gadolinium\", \"#157.25\", 11, 9, \"Tb\", \"Terbium\", \"158.92535\", 12, 9, \"Dy\", \"Dysprosium\", \"162.5\", 13, 9, \"Ho\", \"Holmium\", \"164.93032\", 14, 9, \"Er\", \"Erbium\", \"#167.259\", 15, 9, \"Tm\", \"Thulium\", \"168.93421\", 16, 9, \"Yb\", \"Ytterbium\", \"#173.054\", 17, 9, \"Lu\", \"Lutetium\", \"#174.9668\", 18, 9, \"Hf\", \"Hafnium\", \"#178.49\", 4, 6, \"Ta\", \"Tantalum\", \"180.94788\", 5, 6, \"W\", \"Tungsten\", \"#183.84\", 6, 6, \"Re\", \"Rhenium\", \"#186.207\", 7, 6, \"Os\", \"Osmium\", \"#190.23\", 8, 6, \"Ir\", \"Iridium\", \"#192.217\", 9, 6, \"Pt\", \"Platinum\", \"#195.084\", 10, 6, \"Au\", \"Gold\", \"196.966569\", 11, 6, \"Hg\", \"Mercury\", \"#200.59\", 12, 6, \"Tl\", \"Thallium\", \"#204.3833\", 13, 6, \"Pb\", \"Lead\", \"207.2\", 14, 6, \"Bi\", \"Bismuth\", \"#208.9804\", 15, 6, \"Po\", \"Polonium\", \"(209)\", 16, 6, \"At\", \"Astatine\", \"(210)\", 17, 6, \"Rn\", \"Radon\", \"(222)\", 18, 6, \"Fr\", \"Francium\", \"(223)\", 1, 7, \"Ra\", \"Radium\", \"(226)\", 2, 7, \"Ac\", \"Actinium\", \"(227)\", 4, 10, \"Th\", \"Thorium\", \"232.03806\", 5, 10, \"Pa\", \"Protactinium\", \"#231.0588\", 6, 10, \"U\", \"Uranium\", \"238.02891\", 7, 10, \"Np\", \"Neptunium\", \"(237)\", 8, 10, \"Pu\", \"Plutonium\", \"(244)\", 9, 10, \"Am\", \"Americium\", \"(243)\", 10, 10, \"Cm\", \"Curium\", \"(247)\", 11, 10, \"Bk\", \"Berkelium\", \"(247)\", 12, 10, \"Cf\", \"Californium\", \"(251)\", 13, 10, \"Es\", \"Einstenium\", \"(252)\", 14, 10, \"Fm\", \"Fermium\", \"(257)\", 15, 10, \"Md\", \"Mendelevium\", \"(258)\", 16, 10, \"No\", \"Nobelium\", \"(259)\", 17, 10, \"Lr\", \"Lawrencium\", \"(262)\", 18, 10, \"Rf\", \"Rutherfordium\", \"(267)\", 4, 7, \"Db\", \"Dubnium\", \"(268)\", 5, 7, \"Sg\", \"Seaborgium\", \"(271)\", 6, 7, \"Bh\", \"Bohrium\", \"(272)\", 7, 7, \"Hs\", \"Hassium\", \"(270)\", 8, 7, \"Mt\", \"Meitnerium\", \"(276)\", 9, 7, \"Ds\", \"Darmstadium\", \"(281)\", 10, 7, \"Rg\", \"Roentgenium\", \"(280)\", 11, 7, \"Cn\", \"Copernicium\", \"(285)\", 12, 7, \"Uut\", \"Unutrium\", \"(284)\", 13, 7, \"Fl\", \"Flerovium\", \"(289)\", 14, 7, \"Uup\", \"Ununpentium\", \"(288)\", 15, 7, \"Lv\", \"Livermorium\", \"(293)\", 16, 7, \"Uus\", \"Ununseptium\", \"(294)\", 17, 7, \"Uuo\", \"Ununoctium\", \"(294)\", 18, 7 ]; var camera, scene, renderer; var controls; var objects = []; var targets = { table: [], sphere: [], helix: [], grid: [] }; init(); animate(); function init() { camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 10000 ); camera.position.z = 3000; scene = new THREE.Scene(); // table for ( var i = 0; i < table.length; i += 5 ) { var element = document.createElement( 'div' ); element.className = 'element'; element.style.backgroundColor = 'rgba(0,127,127,' + ( Math.random() * 0.5 + 0.25 ) + ')'; var number = document.createElement( 'div' ); number.className = 'number'; number.textContent = (i/5) + 1; element.appendChild( number ); var symbol = document.createElement( 'div' ); symbol.className = 'symbol'; symbol.textContent = table[ i ]; element.appendChild( symbol ); var details = document.createElement( 'div' ); details.className = 'details'; details.innerHTML = table[ i + 1 ] + '' + table[ i + 2 ]; element.appendChild( details ); var object = new THREE.CSS3DObject( element ); object.position.x = Math.random() * 4000 - 2000; object.position.y = Math.random() * 4000 - 2000; object.position.z = Math.random() * 4000 - 2000; scene.add( object ); objects.push( object ); // var object = new THREE.Object3D(); object.position.x = ( table[ i + 3 ] * 140 ) - 1330; object.position.y = - ( table[ i + 4 ] * 180 ) + 990; targets.table.push( object ); } // sphere var vector = new THREE.Vector3(); for ( var i = 0, l = objects.length; i < l; i ++ ) { var phi = Math.acos( -1 + ( 2 * i ) / l ); var theta = Math.sqrt( l * Math.PI ) * phi; var object = new THREE.Object3D(); object.position.x = 800 * Math.cos( theta ) * Math.sin( phi ); object.position.y = 800 * Math.sin( theta ) * Math.sin( phi ); object.position.z = 800 * Math.cos( phi ); vector.copy( object.position ).multiplyScalar( 2 ); object.lookAt( vector ); targets.sphere.push( object ); } // helix var vector = new THREE.Vector3(); for ( var i = 0, l = objects.length; i < l; i ++ ) { var phi = i * 0.175 + Math.PI; var object = new THREE.Object3D(); object.position.x = 900 * Math.sin( phi ); object.position.y = - ( i * 8 ) + 450; object.position.z = 900 * Math.cos( phi ); vector.x = object.position.x * 2; vector.y = object.position.y; vector.z = object.position.z * 2; object.lookAt( vector ); targets.helix.push( object ); } // grid for ( var i = 0; i < objects.length; i ++ ) { var object = new THREE.Object3D(); object.position.x = ( ( i % 5 ) * 400 ) - 800; object.position.y = ( - ( Math.floor( i / 5 ) % 5 ) * 400 ) + 800; object.position.z = ( Math.floor( i / 25 ) ) * 1000 - 2000; targets.grid.push( object ); } // renderer = new THREE.CSS3DRenderer(); renderer.setSize( window.innerWidth, window.innerHeight ); renderer.domElement.style.position = 'absolute'; document.getElementById( 'container' ).appendChild( renderer.domElement ); // controls = new THREE.TrackballControls( camera, renderer.domElement ); controls.rotateSpeed = 0.5; controls.minDistance = 500; controls.maxDistance = 6000; controls.addEventListener( 'change', render ); var button = document.getElementById( 'table' ); button.addEventListener( 'click', function ( event ) { transform( targets.table, 2000 ); }, false ); var button = document.getElementById( 'sphere' ); button.addEventListener( 'click', function ( event ) { transform( targets.sphere, 2000 ); }, false ); var button = document.getElementById( 'helix' ); button.addEventListener( 'click', function ( event ) { transform( targets.helix, 2000 ); }, false ); var button = document.getElementById( 'grid' ); button.addEventListener( 'click', function ( event ) { transform( targets.grid, 2000 ); }, false ); transform( targets.table, 5000 ); // window.addEventListener( 'resize', onWindowResize, false ); } function transform( targets, duration ) { TWEEN.removeAll(); for ( var i = 0; i < objects.length; i ++ ) { var object = objects[ i ]; var target = targets[ i ]; new TWEEN.Tween( object.position ) .to( { x: target.position.x, y: target.position.y, z: target.position.z }, Math.random() * duration + duration ) .easing( TWEEN.Easing.Exponential.InOut ) .start(); new TWEEN.Tween( object.rotation ) .to( { x: target.rotation.x, y: target.rotation.y, z: target.rotation.z }, Math.random() * duration + duration ) .easing( TWEEN.Easing.Exponential.InOut ) .start(); } new TWEEN.Tween( this ) .to( {}, duration * 2 ) .onUpdate( render ) .start(); } function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize( window.innerWidth, window.innerHeight ); render(); } function animate() { requestAnimationFrame( animate ); TWEEN.update(); controls.update(); } function render() { renderer.render( scene, camera ); } document.onkeydown=function (e){ var currKey=0,evt=e||window.event; currKey=evt.keyCode||evt.which||evt.charCode; if (currKey == 123) { window.event.cancelBubble = true; window.event.returnValue = false; } }"},{"title":"","date":"2019-12-29T06:55:50.751Z","updated":"2019-12-29T06:55:50.751Z","comments":true,"path":"games/cat/index.html","permalink":"https://www.itrhx.com/games/cat/index.html","excerpt":"","text":"圈小猫 | TRHX'S BLOG body {background-color: #eeeeee} #catch-the-cat {width: 100%;text-align: center;} #footer{position:relative;clear:both;padding:10px 20px 40px 0;padding:10px 0;width:100%;text-align:center}#footer address{display:inline-block;padding:2px 10px;color:rgba(0,0,0,.5);font-style:normal} #footer a{color:rgba(0,0,0,.5);cursor:grab}#footer a:hover{border-bottom:1px dotted #00387d;color:#00387d} 游戏:《圈小猫》 window.game = new CatchTheCatGame({ w: 11, h: 11, r: 20, backgroundColor: 0xffffff, parent: 'catch-the-cat', statusBarAlign: 'center', credit: 'www.itrhx.com' }); Copyright © 2018-2019 TRHX'S BLOG |    鄂ICP备19003281号-3 |  中国互联网违法和不良信息举报中心 推荐使用1920*1080分辨率、谷歌或者火狐浏览器浏览此页 |  站点地图 |   |  RSS订阅 |  996.ICU |  申请友链 |  Powered By Hexo |  Hosted By GitHub Pages 部分资料来源于网络,版权属于其合法持有人,只供学习交流之用,非商务用途。如有侵犯您的权益,请及时告知删除。互动交流时请遵守理性,宽容,换位思考的原则。 document.onkeydown=function (e){ var currKey=0,evt=e||window.event; currKey=evt.keyCode||evt.which||evt.charCode; if (currKey == 123) { window.event.cancelBubble = true; window.event.returnValue = false; } }"},{"title":"","date":"2019-12-29T06:55:50.751Z","updated":"2019-12-29T06:55:50.751Z","comments":true,"path":"games/2048/index.html","permalink":"https://www.itrhx.com/games/2048/index.html","excerpt":"","text":"2048 | TRHX'S BLOG 2048 使用方向键操作 New Game score:0 GAME OVER Copyright © 2018-2019 TRHX'S BLOG |    鄂ICP备19003281号-3 |  中国互联网违法和不良信息举报中心 推荐使用1920*1080分辨率、谷歌或者火狐浏览器浏览此页 |  站点地图 |   |  RSS订阅 |  996.ICU |  申请友链 |  Powered By Hexo |  Hosted By GitHub Pages 部分资料来源于网络,版权属于其合法持有人,只供学习交流之用,非商务用途。如有侵犯您的权益,请及时告知删除。互动交流时请遵守理性,宽容,换位思考的原则。 document.onkeydown=function (e){ var currKey=0,evt=e||window.event; currKey=evt.keyCode||evt.which||evt.charCode; if (currKey == 123) { window.event.cancelBubble = true; window.event.returnValue = false; } }"},{"title":"","date":"2019-12-29T06:55:50.754Z","updated":"2019-12-29T06:55:50.754Z","comments":true,"path":"games/piano/index.html","permalink":"https://www.itrhx.com/games/piano/index.html","excerpt":"","text":"网页版钢琴 | TRHX'S BLOG 网页版钢琴 qaz sx dc rfv gb hn jm ik, w e t y u 弹奏方法 使用鼠标左键点击钢琴键,或者键入钢琴键上输入的键盘字母。 Copyright © 2018-2019 TRHX'S BLOG |    鄂ICP备19003281号-3 |  中国互联网违法和不良信息举报中心 推荐使用1920*1080分辨率、谷歌或者火狐浏览器浏览此页 |  站点地图 |   |  RSS订阅 |  996.ICU |  申请友链 |  Powered By Hexo |  Hosted By GitHub Pages 部分资料来源于网络,版权属于其合法持有人,只供学习交流之用,非商务用途。如有侵犯您的权益,请及时告知删除。互动交流时请遵守理性,宽容,换位思考的原则。 document.onkeydown=function (e){ var currKey=0,evt=e||window.event; currKey=evt.keyCode||evt.which||evt.charCode; if (currKey == 123) { window.event.cancelBubble = true; window.event.returnValue = false; } }"},{"title":"","date":"2019-12-29T06:55:50.754Z","updated":"2019-12-29T06:55:50.754Z","comments":true,"path":"games/gobang/index.html","permalink":"https://www.itrhx.com/games/gobang/index.html","excerpt":"","text":"五子棋 | TRHX'S BLOG canvas { display: block; margin: 60px auto; box-shadow: -2px -2px 2px #efefef, 5px 5px 5px #b9b9b9; cursor: pointer; } .btn-wrap { display: flex; flex-direction: row; justify-content: center; } .btn-wrap div { margin: 0 10px; } div>span { display: inline-block; padding: 10px 20px; color: #fff; background-color: #6496ED; border-radius: 5px; cursor: pointer; } div.unable span { background: #D6D6D4; color: #adacaa; } #result-wrap { text-align: center; margin:50px 0 0 0; } #footer{position:relative;clear:both;padding:10px 20px 40px 0;padding:10px 0;width:100%;text-align:center}#footer address{display:inline-block;padding:2px 10px;color:rgba(0,0,0,.5);font-style:normal} #footer a{color:rgba(0,0,0,.5);cursor:grab}#footer a:hover{border-bottom:1px dotted #00387d;color:#00387d} 人机五子棋对弈 重新开始 悔棋 撤销悔棋 var over = false; var me = true; //我 var _nowi = 0, _nowj = 0; //记录自己下棋的坐标 var _compi = 0, _compj = 0; //记录计算机当前下棋的坐标 var _myWin = [], _compWin = []; //记录我,计算机赢的情况 var backAble = false, returnAble = false; var resultTxt = document.getElementById('result-wrap'); var chressBord = []; //棋盘 for (var i = 0; i < 15; i++) { chressBord[i] = []; for (var j = 0; j < 15; j++) { chressBord[i][j] = 0; } } //赢法的统计数组 var myWin = []; var computerWin = []; //赢法数组 var wins = []; for (var i = 0; i < 15; i++) { wins[i] = []; for (var j = 0; j < 15; j++) { wins[i][j] = []; } } var count = 0; //赢法总数 //横线赢法 for (var i = 0; i < 15; i++) { for (var j = 0; j < 11; j++) { for (var k = 0; k < 5; k++) { wins[i][j + k][count] = true; } count++; } } //竖线赢法 for (var i = 0; i < 15; i++) { for (var j = 0; j < 11; j++) { for (var k = 0; k < 5; k++) { wins[j + k][i][count] = true; } count++; } } //正斜线赢法 for (var i = 0; i < 11; i++) { for (var j = 0; j < 11; j++) { for (var k = 0; k < 5; k++) { wins[i + k][j + k][count] = true; } count++; } } //反斜线赢法 for (var i = 0; i < 11; i++) { for (var j = 14; j > 3; j--) { for (var k = 0; k < 5; k++) { wins[i + k][j - k][count] = true; } count++; } } // debugger; for (var i = 0; i < count; i++) { myWin[i] = 0; _myWin[i] = 0; computerWin[i] = 0; _compWin[i] = 0; } var chess = document.getElementById(\"chess\"); var context = chess.getContext('2d'); context.strokeStyle = '#bfbfbf'; //边框颜色 var backbtn = document.getElementById(\"goback\"); var returnbtn = document.getElementById(\"return\"); window.onload = function () { drawChessBoard(); // 画棋盘 } document.getElementById(\"restart\").onclick = function () { window.location.reload(); } // 我,下棋 chess.onclick = function (e) { if (over) { return; } if (!me) { return; } // 悔棋功能可用 backbtn.className = backbtn.className.replace(new RegExp(\"(\\\\s|^)unable(\\\\s|$)\"), \" \"); var x = e.offsetX; var y = e.offsetY; var i = Math.floor(x / 30); var j = Math.floor(y / 30); _nowi = i; _nowj = j; if (chressBord[i][j] == 0) { oneStep(i, j, me); chressBord[i][j] = 1; //我,已占位置 for (var k = 0; k < count; k++) { // 将可能赢的情况都加1 if (wins[i][j][k]) { // debugger; myWin[k]++; _compWin[k] = computerWin[k]; computerWin[k] = 6; //这个位置对方不可能赢了 if (myWin[k] == 5) { // window.alert('你赢了'); resultTxt.innerHTML = '恭喜,你赢了!'; over = true; } } } if (!over) { me = !me; computerAI(); } } } // 悔棋 backbtn.onclick = function (e) { if (!backAble) { return; } over = false; me = true; // resultTxt.innerHTML = 'emmmm,悔棋中'; // 撤销悔棋功能可用 returnbtn.className = returnbtn.className.replace(new RegExp(\"(\\\\s|^)unable(\\\\s|$)\"), \" \"); // 我,悔棋 chressBord[_nowi][_nowj] = 0; //我,已占位置 还原 minusStep(_nowi, _nowj); //销毁棋子 for (var k = 0; k < count; k++) { // 将可能赢的情况都减1 if (wins[_nowi][_nowj][k]) { myWin[k]--; computerWin[k] = _compWin[k]; //这个位置对方可能赢 } }// 计算机相应的悔棋 chressBord[_compi][_compj] = 0; //计算机,已占位置 还原 minusStep(_compi, _compj); //销毁棋子 for (var k = 0; k < count; k++) { // 将可能赢的情况都减1 if (wins[_compi][_compj][k]) { computerWin[k]--; myWin[k] = _myWin[i]; //这个位置对方可能赢 } } resultTxt.innerHTML = '--人机五子棋--'; returnAble = true; backAble = false; } // 撤销悔棋 returnbtn.onclick = function (e) { if (!returnAble) { return; } // 我,撤销悔棋 chressBord[_nowi][_nowj] = 1; //我,已占位置 oneStep(_nowi, _nowj, me); for (var k = 0; k < count; k++) { if (wins[_nowi][_nowj][k]) { myWin[k]++; _compWin[k] = computerWin[k]; computerWin[k] = 6; //这个位置对方不可能赢 } if (myWin[k] == 5) { resultTxt.innerHTML = '恭喜,你赢了!'; over = true; } }// 计算机撤销相应的悔棋 chressBord[_compi][_compj] = 2; //计算机,已占位置 oneStep(_compi, _compj, false); for (var k = 0; k < count; k++) { // 将可能赢的情况都减1 if (wins[_compi][_compj][k]) { computerWin[k]++; _myWin[k] = myWin[k]; myWin[k] = 6; //这个位置对方不可能赢 } if (computerWin[k] == 5) { resultTxt.innerHTML = '很遗憾,计算机赢了,继续加油哦!'; over = true; } } returnbtn.className += ' ' + 'unable'; returnAble = false; backAble = true; } // 计算机下棋 var computerAI = function () { var myScore = []; var computerScore = []; var max = 0; var u = 0, v = 0; for (var i = 0; i < 15; i++) { myScore[i] = []; computerScore[i] = []; for (var j = 0; j < 15; j++) { myScore[i][j] = 0; computerScore[i][j] = 0; } } for (var i = 0; i < 15; i++) { for (var j = 0; j < 15; j++) { if (chressBord[i][j] == 0) { for (var k = 0; k < count; k++) { if (wins[i][j][k]) { if (myWin[k] == 1) { myScore[i][j] += 200; } else if (myWin[k] == 2) { myScore[i][j] += 400; } else if (myWin[k] == 3) { myScore[i][j] += 2000; } else if (myWin[k] == 4) { myScore[i][j] += 10000; } if (computerWin[k] == 1) { computerScore[i][j] += 220; } else if (computerWin[k] == 2) { computerScore[i][j] += 420; } else if (computerWin[k] == 3) { computerScore[i][j] += 2100; } else if (computerWin[k] == 4) { computerScore[i][j] += 20000; } } } if (myScore[i][j] > max) { max = myScore[i][j]; u = i; v = j; } else if (myScore[i][j] == max) { if (computerScore[i][j] > computerScore[u][v]) { u = i; v = j; } } if (computerScore[i][j] > max) { max = computerScore[i][j]; u = i; v = j; } else if (computerScore[i][j] == max) { if (myScore[i][j] > myScore[u][v]) { u = i; v = j; } } } } } _compi = u; _compj = v; oneStep(u, v, false); chressBord[u][v] = 2;//计算机占据位置 for (var k = 0; k < count; k++) { if (wins[u][v][k]) { computerWin[k]++; _myWin[k] = myWin[k]; myWin[k] = 6; //这个位置对方不可能赢了 if (computerWin[k] == 5) { resultTxt.innerHTML = '很遗憾,计算机赢了,继续加油哦!'; over = true; } } } if (!over) { me = !me; } backAble = true; returnAble = false; var hasClass = new RegExp('unable').test(' ' + returnbtn.className + ' '); if (!hasClass) { returnbtn.className += ' ' + 'unable'; } } //绘画棋盘 var drawChessBoard = function () { for (var i = 0; i < 15; i++) { context.moveTo(15 + i * 30, 15); context.lineTo(15 + i * 30, 435); context.stroke(); context.moveTo(15, 15 + i * 30); context.lineTo(435, 15 + i * 30); context.stroke(); } } //画棋子 var oneStep = function (i, j, me) { context.beginPath(); context.arc(15 + i * 30, 15 + j * 30, 13, 0, 2 * Math.PI); // 画圆 context.closePath(); //渐变 var gradient = context.createRadialGradient(15 + i * 30 + 2, 15 + j * 30 - 2, 13, 15 + i * 30 + 2, 15 + j * 30 - 2, 0); if (me) { gradient.addColorStop(0, '#0a0a0a'); gradient.addColorStop(1, '#636766'); } else { gradient.addColorStop(0, '#d1d1d1'); gradient.addColorStop(1, '#f9f9f9'); } context.fillStyle = gradient; context.fill(); } //销毁棋子 var minusStep = function (i, j) { //擦除该圆 context.clearRect((i) * 30, (j) * 30, 30, 30); // 重画该圆周围的格子 context.beginPath(); context.moveTo(15 + i * 30, j * 30); context.lineTo(15 + i * 30, j * 30 + 30); context.moveTo(i * 30, j * 30 + 15); context.lineTo((i + 1) * 30, j * 30 + 15); context.stroke(); } Copyright © 2018-2019 TRHX'S BLOG |    鄂ICP备19003281号-3 |  中国互联网违法和不良信息举报中心 推荐使用1920*1080分辨率、谷歌或者火狐浏览器浏览此页 |  站点地图 |   |  RSS订阅 |  996.ICU |  申请友链 |  Powered By Hexo |  Hosted By GitHub Pages 部分资料来源于网络,版权属于其合法持有人,只供学习交流之用,非商务用途。如有侵犯您的权益,请及时告知删除。互动交流时请遵守理性,宽容,换位思考的原则。 document.onkeydown=function (e){ var currKey=0,evt=e||window.event; currKey=evt.keyCode||evt.which||evt.charCode; if (currKey == 123) { window.event.cancelBubble = true; window.event.returnValue = false; } }"},{"title":"","date":"2020-02-27T12:02:54.700Z","updated":"2020-02-27T12:02:54.700Z","comments":true,"path":"box/index.html","permalink":"https://www.itrhx.com/box/index.html","excerpt":"","text":"百宝箱丨TRHX'S BLOG 开发社区 代码托管 语言文档 技能训练 在线平台 高校平台 游戏编程 HOT Pythoner 文档资料 博客收藏 学习资源 组织社区 爬虫相关 HOT 学习教程 在线视频 博客论坛 学习平台 常用工具 站长工具 HOT IT工具箱 文件处理 HOT 设计素材 效率软件 HOT 服务平台 云服务商 众包平台 站内游戏 HOT 更多导航 关于本页 隐藏/显示侧边栏 博客首页 友情链接 评论留言 关于博主 (function(a,h,g,f,e,d,c,b){b=function(){d=h.createElement(g);c=h.getElementsByTagName(g)[0];d.src=e;d.charset=\"utf-8\";d.async=1;c.parentNode.insertBefore(d,c)};a[\"SeniverseWeatherWidgetObject\"]=f;a[f]||(a[f]=function(){(a[f].q=a[f].q||[]).push(arguments)});a[f].l=+new Date();if(a.attachEvent){a.attachEvent(\"onload\",b)}else{a.addEventListener(\"load\",b,false)}}(window,document,\"script\",\"SeniverseWeatherWidget\",\"//cdn.sencdn.com/widget2/static/js/bundle.js?t=\"+parseInt((new Date().getTime() / 100000000).toString(),10))); window.SeniverseWeatherWidget('show', { flavor: \"slim\", location: \"WX4FBXXFKE4F\", geolocation: true, language: \"auto\", unit: \"c\", theme: \"auto\", token: \"a39cd5a0-4024-4cb2-85c6-0250317058db\", hover: \"enabled\", container: \"tp-weather-widget\" }) 开发社区 Stack Overflow 全球最受程序员欢迎的开发社区 CSDN 全球最大中文IT社区,为IT专业技术人员提供最全面的信息传播和服务平台 博客园 代码改变世界 V2EX V2EX = way to explore 掘金 一个帮助开发者成长的社区 SegmentFault 改变并提升人们获取知识的方式和效率,帮助更多的开发者获得成长与成功 开源中国 国内最大的开源技术社区 ITeye ITeye软件开发交流社区 - Java编程 Spring框架 Ajax技术 agile敏捷软件开发 ruby on rails实践 51CTO 技术成就梦想 ITPUB 全球最大的学习分享平台 知乎 国内最受欢迎的知识性问答社区 简书 创作你的创作 云+社区 来自腾讯的开发者技术分享社区 云栖社区 阿里云面向开发者的开放型技术平台 代码托管 Github 全球最大的面向开源及私有软件项目的托管平台 Gitlab 支持无限的公有项目和私有项目的代码托管平台 Bitbucket 同时支持 Git 和 Mercurial 这两个版本控制软件,免费的私有仓库,支持5人以内的合作开发 SourceForge 又称 SF.net,是开源软件开发者进行开发管理的集中式场所 Coding 国内首个一站式云端软件服务平台 Gitee 国内最大的开源社区 OSChina 的代码托管平台 阿里云代码托管 阿里云旗下代码托管平台 百度效率云 百度云旗下的 Git 代码托管平台 语言文档 Zeal 脱机文档浏览器,包含196种语言API文档,支持Windows、Linux和macOS Dash 适用于Mac OS平台的软件编程文档管理工具,可以浏览API文档,以及管理代码片段工具。自带了丰富的API文档,涉及各种主流的编程语言和框架 DevDocs 在快速,有条理和可搜索的界面中结合了多个API文档,可以在移动设备上离线运行,并且可以安装在Chrome上 C/C++ C/C++ API 文档 C# C# API 文档 Java Java API 文档 .NET .NET API 文档 PHP PHP API 文档 JavaScript JavaScript API 文档 Python Python API 文档 Android Android API 文档 iOS iOS API 文档 SQL SQL API 文档 Swift Swift API 文档 Ruby Ruby API 文档 GO GO API 文档 R R API 文档 MATLAB MATLAB API 文档 Node.js Node.js API 文档 HTML HTML API 文档 CSS CSS API 文档 Redis Redis API 文档 MongoDB MongoDB API 文档 Django Django API 文档 在线平台 LeetCode 全球极客挚爱的技术成长平台 Topcoder 全世界规模最大的程序竞赛网站,也会有一些算法竞赛,适合一些高端的或者搞ACM的,也会举办一些比赛 Codeforces 俄罗斯最大的算法比赛网站 Hihocoder 技术团队来自原北京大学POJ (PKU Online Judge)开发团队,收集了全球范围内很多地区、高校举办的比赛试题, 提供365天*24小时的在线提交、评判程序的服务 LintCode 被称作中文版的leetcode,也是可以做为编程能力提升的一个中转站 SPOJ 波兰的算法刷题网站 NEUQ OJ 一个在线的判题平台 洛谷 创办于2013年,致力于为参加noip、noi、acm的选手提供清爽、快捷的编程体验 牛客网 中国最大的IT题库 C语言网 在这里可以参加包括ACM、NOI在内的各种C/C++/java程序比赛,也可以DIY举办各类程序比赛活动! 计蒜客 计蒜客OI题库致力于为参加noi、noip、信息学竞赛的选手提供优秀的Online Judge系统 高校平台 POJ 北京大学程序在线评测系统 FDU OJ 复旦大学程序在线评测系统 TJ OJ 同济大学程序在线评测系统 USTC OJ 中国科学技术大学程序在线评测系统 ZOJ 浙江大学程序在线评测系统 HDU OJ 杭州电子科技大学程序在线评测系统 CSU-ACM 中南大学程序在线评测系统 HOJX 哈尔滨工业大学程序在线评测系统 HRBUST OJ 哈尔滨理工大学程序在线评测系统 PowerOJ 西南科技大学程序在线评测系统 SCU OJ 四川大学程序在线评测系统 FZU CoidngOJ 福州大学程序在线评测系统 NBUT OJ 宁波工程学院程序在线评测系统 Lutece 电子科技大学程序在线评测系统 武汉大学 ACM 协会 武汉大学 ACM 协会 ZJUT OJ 浙江工业大学程序在线评测系统 游戏编程 CheckiO 面向初学者和高级程序员的编码游戏,使用Python和JavaScript解决棘手的挑战和有趣的任务,从而提高您的编码技能 Coding Games 支持包括PHP、C、JavaScript在内的20多种编程语言。用户界面功能强大,可以定制 Codewars 一个外国的在线练习编程的网站,做题的过程类似打怪,做题升级,而且可以看到别人的解法,里面有很多巧妙的写法可以学习 CodeCombat 一个面向学生的游戏和CS学习平台。这是一个社区项目,有数百玩家自愿提供支持。支持语言包括Java、JS、Python、Lua、CoffeeScript Screeps 在游戏中学习JavaScript。世界上第一款针对程序员的MMO沙盒游戏 VIM Adventures 玩游戏的时候学VIM Cyber-Dojo 一个提供给程序员们练习写程序的地方。支持语言包括JavaScript、Java、Python、PHP、Ruby和很多其他语言 Elevator Saga 电梯编程游戏,跟随关卡解决所有挑站,使用语言为JavaScript Ruby Quiz 一个Ruby程序员提供的每周编程挑战项目 hacker.org 这项挑战由一系列本设计来强化你黑客技巧的解密、诡计、测试、烧脑环节组成。想要通关本系列,你必须学会解密、编码、渗透 Ruby Warrior 玩游戏学Ruby,通过Ruby脚本来控制一个Warrior通过每一关,每一关的代码难度都会有所增加,使玩家逐渐了解Ruby基本的函数、控制、变量、数组等语言特性的用法 文档资料 Python 官方文档 Python 官方文档 Python 标准库 Python 标准库 Python Requests Python Requests 文档 Python Urllib Python Urllib 文档 Python Selenium Python Selenium 中文翻译文档 正则表达式 Python 正则表达式官方文档 Beautiful Soup Beautiful Soup 文档 Scrapy Scrapy 爬虫框架官方文档 PySpider PySpider 爬虫框架官方文档 Matplotlib Matplotlib 2D绘图库 官方中文文档 Numpy Numpy 科学计算 官方中文文档 Pandas Pandas 结构化数据分析 官方中文文档 博客收藏 廖雪峰 廖雪峰的官方网站 - 研究互联网产品和技术,提供原创中文精品教程 崔庆才 崔庆才的个人博客,专注PHP,Python,爬虫,深度学习,机器学习,数据分析 莫烦Python 专注Python、机器学习、深度学习 唐松 专注Python网络爬虫, 数据科学, 数据挖掘, 数据分析 捕蛇者说 编程、程序员、Python FxxkPython 学习python的正确姿势 wistbean Python 大佬 Piglei Python 大佬 TendCode Python 大佬 追梦人物的博客 Python Django 大佬 the5fire 《Django企业开发实战》作者,关注Python、Django、Vim、Linux、Web开发 小明明S À DOMICILE 《Python Web开发实战》作者,Python 大佬 Python之禅 Python 大佬 Python 知识圈 Python知识圈 - 实用的Python教程网站 Python 教程网 小詹学Python,专注Python学习 烂笔头 j_hao104 Python大佬 咸鱼日常 专注Python爬虫,有许多JS逆向文章 AnSheng Python 全栈大佬 夏溪辰 云栖社区特邀爬虫工程师,Python大佬 高级农民工 Python大佬 云爬虫技术研究笔记 Lateautumn4lin 爬虫开发工程师,多年反爬虫破解经验,沉迷数据分析和黑客增长,CSDN博客专家,华为云享专家 云爬虫技术研究笔记(CSDN) Lateautumn4lin 爬虫开发工程师,多年反爬虫破解经验,沉迷数据分析和黑客增长,CSDN博客专家,华为云享专家 Jack Cui CSDN博客专家,Python 大佬 学习资源 Python爬虫人工智能学习教程 Python爬虫人工智能学习教程分享 Python 中文学习大本营 Python 中文学习大本营 Python 资源大全中文版 Python 资源大全中文版 爱湃森 各种 Python 教程 组织社区 PyChina Python 中国社区 PyCon China 中国 Python 开发者大会 蠎周刊 蠎周刊 - 汇集全球蠎事儿 爬虫相关 镀金的天空 GlidedSky 镀金的天空,在线爬虫练习题库 夜幕爬虫安全论坛 一个专注于爬虫与 PC/Web/ 移动端安全领域技术交流的社区,社区由夜幕团队 NightTeam 创办,旨在提升开发者对爬虫与软件安全防护的理解 西刺免费代理IP 每日更新免费HTTP代理,所有代理均为6675端口高匿代理,可隐藏IP 爬虫IP代理池 爬虫IP代理池 云打码 采用全球领先的秒传识别系统,50%图片零秒识别,人工平均处理时间0-3秒 超级鹰 专业的验证码云端识别服务,让验证码识别更快速、更准确、更强大 八爪鱼采集器 一款使用简单、功能强大的网络爬虫工具,完全可视化操作,无需编写代码,内置海量模板,支持任意网络数据抓取 Python 逆向 Python 逆向相关资源 Python 爬虫集合 Python 爬虫集合 Python 入门网络爬虫之精华版 Python 入门网络爬虫之精华版 爬虫项目进阶实战 Python3 爬虫项目进阶实战、JS加解密、逆向教程、css 加密、字体加密 Python 模拟登陆一些大型网站 Python 模拟登陆一些大型网站 系统化学习 Python 爬虫 系统化学习 Python 爬虫 Python3 网络爬虫实战 Python3 网络爬虫实战 在线视频 腾讯课堂 腾讯推出的专业在线教育平台,聚合大量优质教育机构和名师 网易云课堂 网易旗下一个专注职业技能提升的在线学习平台。立足于实用性的要求,与多家教育培训机构和行业的专家、讲师建立合作 中国大学 MOOC 中国大学MOOC(慕课),国家精品课程在线学习平台 黑马程序员 致力于培养中级程序员,是业内以口碑闻名的IT教育培训机构 课工场 更可靠的IT就业教育平台,针对大学生量身定制人工智能、大数据、云计算、区块链、Java大数据开发等大学生IT培训课程 极客学院 极客学院作为中国专业IT职业在线教育平台,拥有海量高清IT职业课程,涵盖30+个技术领域 慕课网 慕课网(IMOOC)是IT技能学习平台。慕课网(IMOOC)提供了丰富的移动端开发、php开发、web前端、android开发以及html5等视频教程资源公开课 尚硅谷 尚硅谷Java培训,谷粉与老学员为你推荐的Java培训、Web前端培训、前端培训、大数据培训、Python培训;0基础入学,学员就业起薪屡创新高! 实验楼 国内领先的IT在线编程及在线实训学习平台,专业导师提供精选的实践项目,创新的技术使得学习者无需配置繁琐的本地环境,随时在线流畅使用 优达学城 Udacity是来自硅谷的前沿技术平台,为广大学子提供WEB前端开发、Python/JAVA编程、IOS/Android开发、人工智能开发等一系列在线课程及实战项目,满足学员灵活的学习需求 51CTO学院 51CTO学院IT职业在线教育平台是依托12年行业品牌、1400万IT技术用户建立的专业IT技能学习培训平台,已签约1000多位技术专家发布了12万个自学式实战视频教程 CSDN 学院 CSDN 学院作为IT在线教育平台,涵盖人工智能、考试认证、移动开发、大数据技术领域职业课程 老男孩IT教育 隶属北京一天天教育科技有限公司,是一直专注于Linux培训、Linux系统及架构师培训、Python培训、网络安全培训,大数据实战的高端培训机构 千锋教育 千锋教育 - 坚持教育初心,坚持面授品质,IT培训良心品牌 博客论坛 鱼C工作室 鱼C工作室-免费编程视频教学|Python教学|Web开发教学|全栈开发教学|C语言教学|汇编教学|Win32开发|加密与解密|Linux教学 吾爱破解 致力于软件安全与病毒分析的前沿,丰富的技术版块交相辉映,由无数热衷于软件加密解密及反病毒爱好者共同维护 廖雪峰 廖雪峰的官方网站 - 研究互联网产品和技术,提供原创中文精品教程 崔庆才 崔庆才的个人博客,专注PHP,Python,爬虫,深度学习,机器学习,数据分析 莫烦Python 专注Python、机器学习、深度学习 唐松 专注Python网络爬虫, 数据科学, 数据挖掘, 数据分析 阮一峰 上海财经大学世界经济博士研究生,计算机科普博主,对自由软件有着坚定不移的信念 学习平台 菜鸟教程 提供了编程的基础技术教程, 介绍了HTML、CSS、Javascript、Python,Java,Ruby,C,PHP , MySQL等各种编程语言的基础知识 W3school 领先的 Web 技术教程 C语言网 C语言网 - 领先实用的编程在线学习网站 前端网 前端网,最好的自学web前端网站 牛客网 牛客网 - 互联网求职神器和备考学习平台 How2J How2J的Java教程, 内容涵盖J2SE、WEB前端、J2EE、框架技术等全面的Java内容 站长工具 新浪短网址 多种后缀短网址生成 百度短网址 百度旗下专业的网址缩短服务 站长工具 - 站长之家 站长工具,SEO工具,权重查询,收录查询,PR查询,ICP备案查询,whois查询,友情链接查询,反向链接查询,网站测试,IP查询,Alexa查询 阿里云 whois 查询 whois查询,域名whois,域名注册信息,whois查询工具,whois信息,域名信息 NnameBeta 国际域名搜索、域名注册、国别域名注册、域名比价 Domcomp 域名比价,Domain Name Price and Availability. 仿站工具箱 在线仿站工具箱 超级 SEO 外链工具 网站自动化宣传机器/免费的超级外链工具可批量增加外链 百度站长平台 百度搜索资源平台 - 让网站更具价值 搜狗站长平台 搜狗站长平台 - 全面掌握在搜狗搜索中的数据表现 360 站长平台 360 站长平台 - 给网站带来更多流量和展现 Google 站长平台 Google 网站站长 - 支持、学习、互动交流和 Search Console – Google Bing 网站管理员工具 Bing 网站管理员工具 百度广告联盟 百度广告联盟为您的流量增值 Google AdSense Google 广告平台 百度统计 百度统计 — 最大的中文网站分析平台 友盟+ 国内领先的第三方全域数据智能服务商 ICP/IP地址/域名信息备案管理系统 工业和信息化部ICP/IP地址/域名信息备案管理系统 全国互联网安全管理服务平台 公安备案网 - 全国互联网安全管理服务平台 IT工具箱 在线工具 - 程序员的工具箱 站长工具、代码格式化、压缩、加密、解密、下载链接转换等 在线工具 - OSCHINA.NET社区 常用文档、常用对照表、代码处理、Html/Js/Css工具、加密/转码工具等 记磊工具箱 Dns检测、CSS格式化、超级Ping、端口扫描等 孟坤工具箱 css一键美化、文本差异比较、代码高亮等 Syntax Highlight Syntax Highlight Code In Word Documents,在Word文档中插入漂亮的代码 Text to ASCII Art Generator Text to ASCII Art Generator,字符串转成 ASCII 码图案 MDEditor 开源在线 Markdown 编辑器 临时邮箱 匿名注册不常用的网站/论坛,保护隐私免骚扰 SM.MS SM 免费图床,每个文件最大支持 5MB 路过图床 免费公共图床,支持最大10MB、批量上传 Greasy Fork 安全、实用的用户脚本大全 Hello World 大全 收集了大约481种 Hello World 程序,涵盖了目前已知的所有编程语言,另加上 67 人类语言 动画展示各种路径搜索算法 动画展示各种路径搜索算法 IT eBooks 可以下载IT电子书籍的网站(英文) GEEKTyper 在线模拟黑客工作的虚拟桌面系统,提供多种黑客工作的场景 免费计算机编程类中文书籍 免费计算机编程类中文书籍 EaseUS Partition Master 磁盘分区管理软件,不用重装系统,就可以重新划分磁盘空间 文件处理 Convertio 在线文件转换工具,支持超过309种不同的文档、图像、电子表格、电子书、文档、演示文稿、音频和视频格式 Office-Converter 免费在线转换视频,在线音频转换,在线图形转换,在线文档转换和在线压缩格式 TinyPNG PNG/JPG图片在线压缩利器 Squoosh Google开源在线压缩、调整工具,支持WebP ILoveIMG 永远免费的在线图片处理工具,可在线编辑,压缩、裁剪、转换、水印等 Smallpdf Smallpdf - A Free Solution to all your PDF Problems,PDF压缩、转换、分割、合并等 PHOTOMOSH 故障艺术在线生成,可以输出jpg、gif和视频 稿定抠图 免费在线抠图软件,图片快速换背景-抠白底图 U钙网 完全免费的LOGO在线设计制作工具 SVGOMG SVG在线压缩平台 在线图片透明圆角处理 在线图片透明圆角处理 草料二维码 国内创建二维码在线应用 Logaster 在线免费创建简单logo及名片设计 Preloaders Loading 懒加载动画在线制作 Loading 制作GIF、SVG、CSS加载动画图标 waifu2x 图片智能无损放大2倍,适合动漫、插画等 智图 腾讯ISUX前端团队开发的一个专门用于图片压缩和图片格式转换的平台 音乐免费下载 全网音乐免费下载工具 OK资源采集 OK资源采集-最新影视资源大全 网易见外工作台 针对视频、图片、文档、音频都可以进行翻译转写操作,每天两小时免费使用 HiPDF 一站式解决所有PDF相关的问题 视频鱼 在线下载各大网站视频的网站 ScreenToGif 开源、轻量级却非常强大的录屏软件,快速将屏幕录制成高清GIF 设计素材 Iconfont 阿里巴巴矢量图标库,提供矢量图标下载、在线存储、格式转换等功能 Font Awesome 一个基于CSS 和 LESS 的字体和图标工具包 Flaticon 海量扁平化免费的图标库 icons8 独特系统平台风格和web图标库,下载免费图标,音乐 千图网 海量原创设计模板免费下载 昵图网 国内海量平面免费素材下载 千库网 免费 png 图片背景素材下载 Pexels 才华横溢的摄影作者在这里免费分享最精彩的素材照片和视频 必应壁纸 必应每日高清壁纸 Piqsels 精美的免版税图库 私藏字体 优质字体免费下载站 第一 PPT 网 免费 PPT 模板下载 吾道幻灯片 全新的office生产力工具,支持演示文稿、PPT模板、协同办公,可以帮助用户轻松创建具有视觉吸引力的幻灯片 Mixkit 免费、高质量、可商用的视频素材分享网站 The Stocks 对各大图片网站进行整合,免费优质图片下载 极简壁纸 高质量精品壁纸网站 NASA Image and Video Library 美国国家航天局的官方库,从此太空类的素材再也不是问题 Unsplash 质量超高的免费图片素材库,无需注册,直接下载 WordArt 文字云工具 效率软件 分流抢票 全程自动抢票,自动抢候补,自动识别验证码,多线程秒单、稳定捡漏,支持多天、多车次、多席别、多乘客等功能 PanDownload 百度网盘下载神器 Quicker 为常用操作建立捷径,PC 快捷动作面板,让效率触手可及! 万彩办公大师 免费、轻松处理文档/音视频/图片的工具 LICEcap 简洁易用的动画屏幕录制软件,它可将屏幕录像的内容直接保存为高质量(每帧颜色数量可超过256)GIF动态图片格式 Snipaste 简单但强大的截图工具,支持截图 + 贴图 FSCapture 一个强大的,轻量级的,功能齐全的屏幕捕获工具 Everything 速度最快的的文件搜索工具 DeskPins 顶置任意窗口 TrafficMonitor 一个用于显示当前网速、CPU及内存利用率的桌面悬浮窗软件 PicGo 由 electronic-vue 构建的简单而精美的图片上传工具 PowerToys 微软为 Windows 系统推出的一系列免费实用小工具合集 Dism++ 一款根据微软底层的架构结构设计的一个系统维护工具,全球第一款基于 CBS 的 Dism GUI 实现 ColorPix 屏幕取色小工具 CCleaner 一款免费的系统优化和隐私保护工具 GifCam 集录制与剪辑为一体的屏幕 GIF 动画制作工具,录制后的动画可以逐帧编辑 EV录屏 一款免费并且不添加水印的录屏工具 Fliqlo 一款极简主义的时钟屏保软件 Fences 栅栏管理桌面,使桌面更加整洁有条理 Q-dir 多窗口文件整理工具 WGestures 鼠标手势工具 XMind 一个全功能的思维导图和头脑风暴软件 速盘 免登录,自动查询提取码,极速的度盘下载工具 f.lux 国外开源的护眼软件,通过根据时间调节屏幕颜色,减少蓝光对视力的影响 云服务商 阿里云 阿里云 - 为了无法计算的价值 腾讯云 腾讯云 - 产业智变 云启未来 百度云 百度云 - 计算无限可能 华为云 华为云 - +智能,见未来 京东云 京东云 - 遇见无限可能 西部数码 西部数码 - 云服务器、虚拟主机、域名注册17年知名云计算服务提供商! 景安云 景安云 - 专业的数据中心服务商 七牛云 七牛云 - 国内领先的企业级云服务商 又拍云 又拍云 - 加速在线业务-CDN-云存储 美橙互联 美橙互联 - 域名注册、企业建站、云服务器、企业网络推广整体解决方案服务商! UCloud UCloud - 中立 安全 可信赖的云计算服务商 AWS AWS 云服务 - 专业的大数据和云计算服务以及云解决方案提供商 Microsoft Azure Azure. Invent with purpose. GoDaddy GoDaddy - 提供域名注册和互联网主机服务的美国公司 Cloudflare Cloudflare - 网络性能和安全公司 jsDelivr jsDelivr - A free, fast, and reliable Open Source CDN for npm and GitHub 众包平台 猿急送 专注于 IT 众包领域,职位内容大多集中于 UI 设计、产品设计、程序开发、产品运营等需求 开源众包 开源中国旗下外包网站,项目大多是团队的整包项目,适合多人组团接单 外包大师 PMCAFF旗下的一个众包开发平台,目前以技术开发为主,以众包开发和自有开发相结合形式运营 人人开发 集可视化开发,应用市场,威客众包,PaaS云于一体的企业级应用服务平台 快码 提供智能硬件、各种智能共享项目解决方案,为互联网创业者提供APP、小程序、公众号开发。 我爱方案网 专注于硬件类外包,电子方案开发供应链众包平台,软件外包,方案,硬件开发方案,硬件设计开发 英选 提供可信赖的定制开发外包服务,包括企业品牌官网、电商系统及创新定制产品开发 智筹 为企业&创业者提供互联网高级人才直租服务。按次直租,解决临时、突发问题;按月直租,建立长期兼职合作;按任务直租,解决有明确预算的外包任务 开发邦 互联网软件定制开发与软件外包开发服务,十年互联网软件定制开发经验 码市 Coding 推出的互联网软件外包服务平台,意在连接需求方与广大开发者。让项目的需求方快速的找到合适的开发者,完成项目开发工作 自由职客 自由职客是权威的IT互联网行业灵活用工交易平台,外包,众包,兼职,招聘,erp,sap 解放号 解放号众包平台提供软件开发外包、人力驻场服务等软件项目外包服务。解放号的软件项目交付全流程可视化监控与全生命周期管理能力 程序员客栈 领先的程序员自由工作平台,38万+优秀开发者,您的专属云端开发团队,BAT级别的开发者,标准化的服务和交付 码易 智网易联旗下IT软件服务平台,集软件商城、企业应用、电商软件、crm软件、商务服务平台于一体的一站式软件外包开发服务平台 电鸭社区 电鸭社区旨在推动自由工作方式在国内渐进式发展,区别于传统方式的工作职位,倡导「只工作,不上班」的工作心态 Sxsoft 中国最早的外包服务平台,18年口碑服务,20万程序员、100+专业软件开发公司,专注解决各类软件开发需求 实现网 为企业提供BAT等名企背景的、靠谱的开发设计兼职人才和自由职业者,满足企业项目外包、驻场开发、远程兼职、技术咨询等短期人力需求 智城外包网 零佣金开发资源平台,认证担保,全程无忧,专业的软件外包网和项目外包、项目开发、人力外派、短期招聘、人力资源交易平台 站内游戏 2048 网页版 2048 小游戏 圈小猫 点击圆点围住小猫 3D 元素周期表 3D 网页展示元素周期表 五子棋 网页版简易五子棋 吃豆人 躲避怪物,吃掉豆子 网页钢琴 简易网页版钢琴 更多导航 创造狮导航 创造狮,一个创意工作者的导航,专注分享正版优质设计、前端、产品、运营的书签导航,设计教程、设计规范、颜色搭配、灵感创意、前端框架、开发者工具、互联网新品推荐、运营数据分析、自媒体和工具利器好用的分类导航大全 大数据导航 大数据导航,以大数据产业为主,大数据工具为辅,给用户提供一个更加快速找到大数据相关的工具平台 优设导航 优设网站导航为设计师提供ps教程、UI设计、素材下载、高清图库、配色方案、用户体验、网页设计等全方位设计师网站导航指引 牛导航 实用工具导航 聚BT 聚BT - 聚合最优质的BT、磁力资源 ShareHub ShareHub - 资源和工具的集合 狼牌工作网址导航 工具,资源,方法,All IN ONE的办公工作网址导航 COPYRIGHT 2018 - 2020 WEBSTACK 丨 DESIGNED BY VIGGO 丨 CHANGED BY TRHX $(document).ready(function() { $(document).on('click', '.has-sub', function(){ var _this = $(this) if(!$(this).hasClass('expanded')) { setTimeout(function(){ _this.find('ul').attr(\"style\",\"\") }, 300); } else { $('.has-sub ul').each(function(id,ele){ var _that = $(this) if(_this.find('ul')[0] != ele) { setTimeout(function(){ _that.attr(\"style\",\"\") }, 300); } }) } }) $('.user-info-menu .hidden-sm').click(function(){ if($('.sidebar-menu').hasClass('collapsed')) { $('.has-sub.expanded > ul').attr(\"style\",\"\") } else { $('.has-sub.expanded > ul').show() } }) $(\"#main-menu li ul li\").click(function() { $(this).siblings('li').removeClass('active'); // 删除其他兄弟元素的样式 $(this).addClass('active'); // 添加当前元素的样式 }); $(\"a.smooth\").click(function(ev) { ev.preventDefault(); public_vars.$mainMenu.add(public_vars.$sidebarProfile).toggleClass('mobile-is-visible'); ps_destroy(); $(\"html, body\").animate({ scrollTop: $($(this).attr(\"href\")).offset().top - 30 }, { duration: 500, easing: \"swing\" }); }); return false; }); var href = \"\"; var pos = 0; $(\"a.smooth\").click(function(e) { $(\"#main-menu li\").each(function() { $(this).removeClass(\"active\"); }); $(this).parent(\"li\").addClass(\"active\"); e.preventDefault(); href = $(this).attr(\"href\"); pos = $(href).position().top - 30; });"}],"posts":[{"title":"前程无忧招聘信息爬取 + 数据可视化","slug":"A90-pyspider-51job","date":"2020-07-13T13:07:35.909Z","updated":"2020-08-06T03:35:57.290Z","comments":true,"path":"2020/07/13/A90-pyspider-51job/","link":"","permalink":"https://www.itrhx.com/2020/07/13/A90-pyspider-51job/","excerpt":"","text":"爬取时间:2020-07-11 实现目标:根据用户输入的关键字爬取相关职位信息存入 MongoDB,读取数据进行可视化展示。 涉及知识:请求库 requests、Xpath 语法、数据库 MongoDB、数据处理与可视化 Numpy、Pandas、Matplotlib。 完整代码:https://github.com/TRHX/Python3-Spider-Practice/tree/master/51job 其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice 爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278 【1x00】获取数据 get_51job_data.py【01x01】构建请求地址以 Python 职位为例,请求地址如下: 第一页:https://search.51job.com/list/000000,000000,0000,00,9,99,python,2,1.html 第二页:https://search.51job.com/list/000000,000000,0000,00,9,99,python,2,2.html 第三页:https://search.51job.com/list/000000,000000,0000,00,9,99,python,2,3.html 初始化函数: 1234def __init__(self): self.base_url = 'https://search.51job.com/list/000000,000000,0000,00,9,99,%s,2,%s.html' self.headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.13 Safari/537.36'} self.keyword = input('请输入关键字:') 【01x02】获取总页数在页面的下方给出了该职位一共有多少页,使用 Xpath 和正则表达式提取里面的数字,方便后面翻页爬取使用,注意页面编码为 gbk。 12345678910def tatal_url(self): url = self.base_url % (self.keyword, str(1)) response = requests.get(url=url, headers=self.headers) tree = etree.HTML(response.content.decode('gbk')) # 提取一共有多少页 text = tree.xpath(\"//div[@class='p_in']/span[1]/text()\")[0] number = re.findall('[0-9]', text) number = int(''.join(number)) print('%s职位共有%d页' % (self.keyword, number)) return number 【01x03】提取详情页 URL定义一个 detail_url() 方法,传入总页数,循环提取每一页职位详情页的 URL,将每一个详情页 URL 传递给 parse_data() 方法,用于解析详情页内的具体职位信息。 提取详情页时有以下几种特殊情况: 特殊情况一:如果有前程无忧自己公司的职位招聘信息掺杂在里面,他的详情页结构和普通的不一样,页面编码也有差别。 页面示例:https://51rz.51job.com/job.html?jobid=115980776 页面真实数据请求地址类似于:https://coapi.51job.com/job_detail.php?jsoncallback=&key=&sign=params={"jobid":""} 请求地址中的各参数值通过 js 加密:https://js.51jobcdn.com/in/js/2018/coapi/coapi.min.js 特殊情况二:部分公司有自己的专属页面,此类页面的结构也不同于普通页面 页面示例:http://dali.51ideal.com/jobdetail.html?jobid=121746338 为了规范化,本次爬取将去掉这部分特殊页面,仅爬取 URL 带有 jobs.51job.com 的数据 123456789101112131415161718192021def detail_url(self, number): for num in range(1, number+1): url = self.base_url % (self.keyword, str(num)) response = requests.get(url=url, headers=self.headers) tree = etree.HTML(response.content.decode('gbk')) detail_url1 = tree.xpath(\"//div[@class='dw_table']/div[@class='el']/p/span/a/@href\") \"\"\" 深拷贝一个 url 列表,如果有连续的不满足要求的链接,若直接在原列表里面删除, 则会漏掉一些链接,因为每次删除后的索引已改变,因此在原列表中提取不符合元素 后,在深拷贝的列表里面进行删除。最后深拷贝的列表里面的元素均符合要求。 \"\"\" detail_url2 = copy.deepcopy(detail_url1) for url in detail_url1: if 'jobs.51job.com' not in url: detail_url2.remove(url) self.parse_data(detail_url2) print('第%d页数据爬取完毕!' % num) time.sleep(2) print('所有数据爬取完毕!') 【01x04】提取职位信息解析详情页时页面编码是 gbk,但是某些页面在解析时仍然会报编码错误,因此使用 try-except 语句捕捉编码错误(UnicodeDecodeError),如果该页面有编码错误则直接 return 结束函数。 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364def parse_data(self, urls): \"\"\" position: 职位 wages: 工资 region: 地区 experience: 经验 education: 学历 need_people: 招聘人数 publish_date: 发布时间 english: 英语要求 welfare_tags: 福利标签 job_information: 职位信息 work_address: 上班地址 company_name: 公司名称 company_nature: 公司性质 company_scale: 公司规模 company_industry: 公司行业 company_information: 公司信息 \"\"\" for url in urls: response = requests.get(url=url, headers=self.headers) try: text = response.content.decode('gbk') except UnicodeDecodeError: return tree = etree.HTML(text) \"\"\" 提取内容时使用 join 方法将列表转为字符串,而不是直接使用索引取值, 这样做的好处是遇到某些没有的信息直接留空而不会报错 \"\"\" position = ''.join(tree.xpath(\"//div[@class='cn']/h1/text()\")) wages = ''.join(tree.xpath(\"//div[@class='cn']/strong/text()\")) # 经验、学历、招聘人数、发布时间等信息都在一个标签里面,逐一使用列表解析式提取 content = tree.xpath(\"//div[@class='cn']/p[2]/text()\") content = [i.strip() for i in content] if content: region = content[0] else: region = '' experience = ''.join([i for i in content if '经验' in i]) education = ''.join([i for i in content if i in '本科大专应届生在校生硕士']) need_people = ''.join([i for i in content if '招' in i]) publish_date = ''.join([i for i in content if '发布' in i]) english = ''.join([i for i in content if '英语' in i]) welfare_tags = ','.join(tree.xpath(\"//div[@class='jtag']/div//text()\")[1:-2]) job_information = ''.join(tree.xpath(\"//div[@class='bmsg job_msg inbox']/p//text()\")).replace(' ', '') work_address = ''.join(tree.xpath(\"//div[@class='bmsg inbox']/p//text()\")) company_name = ''.join(tree.xpath(\"//div[@class='tCompany_sidebar']/div[1]/div[1]/a/p/text()\")) company_nature = ''.join(tree.xpath(\"//div[@class='tCompany_sidebar']/div[1]/div[2]/p[1]//text()\")) company_scale = ''.join(tree.xpath(\"//div[@class='tCompany_sidebar']/div[1]/div[2]/p[2]//text()\")) company_industry = ''.join(tree.xpath(\"//div[@class='tCompany_sidebar']/div[1]/div[2]/p[3]/@title\")) company_information = ''.join(tree.xpath(\"//div[@class='tmsg inbox']/text()\")) job_data = [position, wages, region, experience, education, need_people, publish_date, english, welfare_tags, job_information, work_address, company_name, company_nature, company_scale, company_industry, company_information] save_mongodb(job_data) 【01x05】保存数据到 MongoDB指定一个名为 job51_spider 的数据库和一个名为 data 的集合,依次将信息保存至 MongoDB。 1234567891011121314151617181920212223def save_mongodb(data): client = pymongo.MongoClient(host='localhost', port=27017) db = client.job51_spider collection = db.data save_data = { '职位': data[0], '工资': data[1], '地区': data[2], '经验': data[3], '学历': data[4], '招聘人数': data[5], '发布时间': data[6], '英语要求': data[7], '福利标签': data[8], '职位信息': data[9], '上班地址': data[10], '公司名称': data[11], '公司性质': data[12], '公司规模': data[13], '公司行业': data[14], '公司信息': data[15] } collection.insert_one(save_data) 【2x00】数据可视化 draw_bar_chart.py【02x01】数据初处理从 MongoDB 里面读取数据为 DataFrame 对象,本次可视化只分析工资与经验、学历的关系,所以只取这三项,由于获取的数据有些是空白值,因此使用 replace 方法将空白值替换成缺失值(NaN),然后使用 DataFrame 对象的 dropna() 方法删除带有缺失值(NaN)的行。将工资使用 apply 方法,将每个值应用于 wish_data 方法,即对每个值进行清洗。 1234567891011121314151617def processing_data(): # 连接数据库,从数据库读取数据(也可以导出后从文件中读取) client = pymongo.MongoClient(host='localhost', port=27017) db = client.job51_spider collection = db.data # 读取数据并转换为 DataFrame 对象 data = pd.DataFrame(list(collection.find())) data = data[['工资', '经验', '学历']] # 使用正则表达式选择空白的字段并填充为缺失值,然后删除带有缺失值的所有行 data.replace(to_replace=r'^\\s*$', value=np.nan, regex=True, inplace=True) data = data.dropna() # 对工资数据进行清洗,处理后的工作单位:元/月 data['工资'] = data['工资'].apply(wish_data) return data 【02x02】数据清洗12345678910111213141516171819202122232425262728293031323334353637383940414243def wish_data(wages_old): \"\"\" 数据清洗规则: 分为元/天,千(以上/下)/月,万(以上/下)/月,万(以上/下)/年 若数据是一个区间的,则求其平均值,最后的值统一单位为元/月 \"\"\" if '元/天' in wages_old: if '-' in wages_old.split('元')[0]: wages1 = wages_old.split('元')[0].split('-')[0] wages2 = wages_old.split('元')[0].split('-')[1] wages_new = (float(wages2) + float(wages1)) / 2 * 30 else: wages_new = float(wages_old.split('元')[0]) * 30 return wages_new elif '千/月' in wages_old or '千以下/月' in wages_old or '千以上/月' in wages_old: if '-' in wages_old.split('千')[0]: wages1 = wages_old.split('千')[0].split('-')[0] wages2 = wages_old.split('千')[0].split('-')[1] wages_new = (float(wages2) + float(wages1)) / 2 * 1000 else: wages_new = float(wages_old.split('千')[0]) * 1000 return wages_new elif '万/月' in wages_old or '万以下/月' in wages_old or '万以上/月' in wages_old: if '-' in wages_old.split('万')[0]: wages1 = wages_old.split('万')[0].split('-')[0] wages2 = wages_old.split('万')[0].split('-')[1] wages_new = (float(wages2) + float(wages1)) / 2 * 10000 else: wages_new = float(wages_old.split('万')[0]) * 10000 return wages_new elif '万/年' in wages_old or '万以下/年' in wages_old or '万以上/年' in wages_old: if '-' in wages_old.split('万')[0]: wages1 = wages_old.split('万')[0].split('-')[0] wages2 = wages_old.split('万')[0].split('-')[1] wages_new = (float(wages2) + float(wages1)) / 2 * 10000 / 12 else: wages_new = float(wages_old.split('万')[0]) * 10000 / 12 return wages_new 【02x03】绘制经验与平均薪资关系图1234567891011121314151617181920212223242526def wages_experience_chart(data): # 根据经验分类,求不同经验对应的平均薪资 wages_experience = data.groupby('经验').mean() # 获取经验和薪资的值,将其作为画图的 x 和 y 数据 w = wages_experience['工资'].index.values e = wages_experience['工资'].values # 按照经验对数据重新进行排序,薪资转为 int 类型(也可以直接在前面对 DataFrame 按照薪资大小排序) wages = [w[6], w[1], w[2], w[3], w[4], w[5], w[0]] experience = [int(e[6]), int(e[1]), int(e[2]), int(e[3]), int(e[4]), int(e[5]), int(e[0])] # 绘制柱状图 plt.rcParams['font.sans-serif'] = ['Microsoft YaHei'] plt.figure(figsize=(9, 6)) x = wages y = experience color = ['#E41A1C', '#377EB8', '#4DAF4A', '#984EA3', '#FF7F00', '#FFFF33', '#A65628'] plt.bar(x, y, color=color) for a, b in zip(x, y): plt.text(a, b, b, ha='center', va='bottom') plt.title('Python 相关职位经验与平均薪资关系', fontsize=13) plt.xlabel('经验', fontsize=13) plt.ylabel('平均薪资(元 / 月)', fontsize=13) plt.savefig('wages_experience_chart.png') plt.show() 【02x04】绘制学历与平均薪资关系图12345678910111213141516171819202122def wages_education_chart(data): # 根据学历分类,求不同学历对应的平均薪资 wages_education = data.groupby('学历').mean() # 获取学历和薪资的值,将其作为画图的 x 和 y 数据 wages = wages_education['工资'].index.values education = [int(i) for i in wages_education['工资'].values] # 绘制柱状图 plt.rcParams['font.sans-serif'] = ['Microsoft YaHei'] plt.figure(figsize=(9, 6)) x = wages y = education color = ['#E41A1C', '#377EB8', '#4DAF4A'] plt.bar(x, y, color=color) for a, b in zip(x, y): plt.text(a, b, b, ha='center', va='bottom') plt.title('Python 相关职位学历与平均薪资关系', fontsize=13) plt.xlabel('学历', fontsize=13) plt.ylabel('平均薪资(元 / 月)', fontsize=13) plt.savefig('wages_education_chart.png') plt.show() 【3x00】数据截图一共有 34009 条数据,完整数据已放在 github,可自行下载。 MongoDB: CSV 文件: JSON 文件: 关系图: 【4x00】完整代码完整代码地址(点亮 star 有 buff 加成):https://github.com/TRHX/Python3-Spider-Practice/tree/master/51job 其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice 爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫实战","slug":"Python3-学习笔记/爬虫实战","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫实战/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"前程无忧","slug":"前程无忧","permalink":"https://www.itrhx.com/tags/前程无忧/"},{"name":"数据可视化","slug":"数据可视化","permalink":"https://www.itrhx.com/tags/数据可视化/"}]},{"title":"COVID-19 肺炎疫情数据实时监控(Python 爬虫 + Pyecharts 数据可视化 + Wordcloud 词云图)","slug":"A89-COVID-19","date":"2020-07-06T04:25:20.285Z","updated":"2020-08-06T03:34:40.274Z","comments":true,"path":"2020/07/06/A89-COVID-19/","link":"","permalink":"https://www.itrhx.com/2020/07/06/A89-COVID-19/","excerpt":"","text":"12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/107140534未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【1x00】前言本来两三个月之前就想搞个疫情数据实时数据展示的,由于各种不可抗拒因素一而再再而三的鸽了,最近终于抽空写了一个,数据是用 Python 爬取的百度疫情实时大数据报告,请求库用的 requests,解析用的 Xpath 语法,词云用的 wordcloud 库,数据可视化用 pyecharts 绘制的地图和折线图,数据储存在 Excel 表格里面,使用 openpyxl 对表格进行处理。 本程序实现了累计确诊地图展示和每日数据变化折线图展示,其他更多数据的获取和展示均可在程序中进行拓展,可以将程序部署在服务器上,设置定时运行,即可实时展示数据,pyecharts 绘图模块也可以整合到 Web 框架(Django、Flask等)中使用。 在获取数据时有全球和境外两个概念,全球包含中国,境外不包含中国,后期绘制的四个图:中国累计确诊地图、全球累计确诊地图(包含中国)、中国每日数据折线图、境外每日数据折线图(不包含中国)。 注意项:直接向该网页发送请求获取的响应中,没有每个国家的每日数据,该数据获取的地址是:https://voice.baidu.com/newpneumonia/get?target=trend&isCaseIn=1&stage=publish 预览地址:http://cov.itrhx.com/ 数据来源:https://voice.baidu.com/act/newpneumonia/newpneumonia/ pyecharts 文档:https://pyecharts.org/ openpyxl 文档:https://openpyxl.readthedocs.io/ wordcloud 文档:http://amueller.github.io/word_cloud/ 【2x00】思维导图 【3x00】数据结构分析通过查看百度的疫情数据页面,可以看到很多整齐的数据,猜测就是疫情相关的数据,保存该页面,对其进行格式化,很容易可以分析出所有的数据都在 <script type="application/json" id="captain-config"></script> 里面,其中 title 里面是一些 Unicode 编码,将其转为中文后更容易得到不同的分类数据。 由于数据繁多,可以将数据主体部分提取出来,删除一些重复项和其他杂项,留下数据大体位置并分析数据结构,便于后期的数据提取,经过处理后的数据大致结构如下: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230<script type=\"application/json\" id=\"captain-config\"> { \"component\": [ { \"mapLastUpdatedTime\": \"2020.07.05 16:13\", // 国内疫情数据最后更新时间 \"caseList\": [ // caseList 列表,每一个元素是一个字典 { \"confirmed\": \"1\", // 每个字典包含中国每个省的每一项疫情数据 \"died\": \"0\", \"crued\": \"1\", \"relativeTime\": \"1593792000\", \"confirmedRelative\": \"0\", \"diedRelative\": \"0\", \"curedRelative\": \"0\", \"curConfirm\": \"0\", \"curConfirmRelative\": \"0\", \"icuDisable\": \"1\", \"area\": \"西藏\", \"subList\": [ // subList 列表,每一个元素是一个字典 { \"city\": \"拉萨\", // 每个字典包含该省份对应的每个城市疫情数据 \"confirmed\": \"1\", \"died\": \"0\", \"crued\": \"1\", \"confirmedRelative\": \"0\", \"curConfirm\": \"0\", \"cityCode\": \"100\" } ] } ], \"caseOutsideList\": [ // caseOutsideList 列表,每一个元素是一个字典 { \"confirmed\": \"241419\", // 每个字典包含各国的每一项疫情数据 \"died\": \"34854\", \"crued\": \"191944\", \"relativeTime\": \"1593792000\", \"confirmedRelative\": \"223\", \"curConfirm\": \"14621\", \"icuDisable\": \"1\", \"area\": \"意大利\", \"subList\": [ // subList 列表,每一个元素是一个字典 { \"city\": \"伦巴第\", // 每个字典包含每个国家对应的每个城市疫情数据 \"confirmed\": \"94318\", \"died\": \"16691\", \"crued\": \"68201\", \"curConfirm\": \"9426\" } ] } ], \"summaryDataIn\": { // summaryDataIn 国内总的疫情数据 \"confirmed\": \"85307\", \"died\": \"4648\", \"cured\": \"80144\", \"asymptomatic\": \"99\", \"asymptomaticRelative\": \"7\", \"unconfirmed\": \"7\", \"relativeTime\": \"1593792000\", \"confirmedRelative\": \"19\", \"unconfirmedRelative\": \"1\", \"curedRelative\": \"27\", \"diedRelative\": \"0\", \"icu\": \"6\", \"icuRelative\": \"0\", \"overseasInput\": \"1931\", \"unOverseasInputCumulative\": \"83375\", \"overseasInputRelative\": \"6\", \"unOverseasInputNewAdd\": \"13\", \"curConfirm\": \"515\", \"curConfirmRelative\": \"-8\", \"icuDisable\": \"1\" }, \"summaryDataOut\": { // summaryDataOut 国外总的疫情数据 \"confirmed\": \"11302569\", \"died\": \"528977\", \"curConfirm\": \"4410601\", \"cured\": \"6362991\", \"confirmedRelative\": \"206165\", \"curedRelative\": \"190018\", \"diedRelative\": \"4876\", \"curConfirmRelative\": \"11271\", \"relativeTime\": \"1593792000\" }, \"trend\": { // trend 字典,包含国内每日的疫情数据 \"updateDate\": [], // 日期 \"list\": [ // list 列表,每项数据及其对应的值 { \"name\": \"确诊\", \"data\": [] }, { \"name\": \"疑似\", \"data\": [] }, { \"name\": \"治愈\", \"data\": [] }, { \"name\": \"死亡\", \"data\": [] }, { \"name\": \"新增确诊\", \"data\": [] }, { \"name\": \"新增疑似\", \"data\": [] }, { \"name\": \"新增治愈\", \"data\": [] }, { \"name\": \"新增死亡\", \"data\": [] }, { \"name\": \"累计境外输入\", \"data\": [] }, { \"name\": \"新增境外输入\", \"data\": [] } ] }, \"foreignLastUpdatedTime\": \"2020.07.05 16:13\", // 国外疫情数据最后更新时间 \"globalList\": [ // globalList 列表,每一个元素是一个字典 { \"area\": \"亚洲\", // 按照不同洲进行分类 \"subList\": [ // subList 列表,每个洲各个国家的疫情数据 { \"died\": \"52\", \"confirmed\": \"6159\", \"crued\": \"4809\", \"curConfirm\": \"1298\", \"confirmedRelative\": \"0\", \"relativeTime\": \"1593792000\", \"country\": \"塔吉克斯坦\" } ], \"died\": \"56556\", // 每个洲总的疫情数据 \"crued\": \"1625562\", \"confirmed\": \"2447873\", \"curConfirm\": \"765755\", \"confirmedRelative\": \"60574\" }, { \"area\": \"其他\", // 其他特殊区域疫情数据 \"subList\": [ { \"died\": \"13\", \"confirmed\": \"712\", \"crued\": \"651\", \"curConfirm\": \"48\", \"confirmedRelative\": \"0\", \"relativeTime\": \"1593792000\", \"country\": \"钻石公主号邮轮\" } ], \"died\": \"13\", // 其他特殊区域疫情总的数据 \"crued\": \"651\", \"confirmed\": \"712\", \"curConfirm\": \"48\", \"confirmedRelative\": \"0\" }, { \"area\": \"热门\", // 热门国家疫情数据 \"subList\": [ { \"died\": \"5206\", \"confirmed\": \"204610\", \"crued\": \"179492\", \"curConfirm\": \"19912\", \"confirmedRelative\": \"1172\", \"relativeTime\": \"1593792000\", \"country\": \"土耳其\" } ], \"died\": \"528967\", // 热门国家疫情总的数据 \"crued\": \"6362924\", \"confirmed\": \"11302357\", \"confirmedRelative\": \"216478\", \"curConfirm\": \"4410466\" }], \"allForeignTrend\": { // allForeignTrend 字典,包含国外每日的疫情数据 \"updateDate\": [], // 日期 \"list\": [ // list 列表,每项数据及其对应的值 { \"name\": \"累计确诊\", \"data\": [] }, { \"name\": \"治愈\", \"data\": [] }, { \"name\": \"死亡\", \"data\": [] }, { \"name\": \"现有确诊\", \"data\": [] }, { \"name\": \"新增确诊\", \"data\": [] } ] }, \"topAddCountry\": [ // 确诊增量最高的国家 { \"name\": \"美国\", \"value\": 53162 } ], \"topOverseasInput\": [ // 境外输入最多的省份 { \"name\": \"黑龙江\", \"value\": 386 } ] } ] }</script> 【4x00】主函数 main()分别将数据获取、词云图绘制、地图绘制写入三个文件:data_get()、data_wordcloud()、data_map(),然后使用一个主函数文件 main.py 来调用这三个文件里面的函数。 1234567891011121314import data_getimport data_wordcloudimport data_mapdata_dict = data_get.init()data_get.china_total_data(data_dict)data_get.global_total_data(data_dict)data_get.china_daily_data(data_dict)data_get.foreign_daily_data(data_dict)data_wordcloud.china_wordcloud()data_wordcloud.global_wordcloud()data_map.all_map() 【5x00】数据获取模块 data_get【5x01】初始化函数 init()使用 xpath 语法 //script[@id="captain-config"]/text() 提取里面的值,利用 json.loads 方法将其转换为字典对象,以便后续的其他函数调用。 1234567891011def init(): headers = { 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.13 Safari/537.36' } url = 'https://voice.baidu.com/act/newpneumonia/newpneumonia/' response = requests.get(url=url, headers=headers) tree = etree.HTML(response.text) dict1 = tree.xpath('//script[@id=\"captain-config\"]/text()') print(type(dict1[0])) dict2 = json.loads(dict1[0]) return dict2 【5x02】中国总数据 china_total_data()12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970def china_total_data(data): \"\"\" 1、中国省/直辖市/自治区/行政区疫情数据 省/直辖市/自治区/行政区:area 现有确诊: curConfirm 累计确诊: confirmed 累计治愈: crued 累计死亡: died 现有确诊增量: curConfirmRelative 累计确诊增量: confirmedRelative 累计治愈增量: curedRelative 累计死亡增量: diedRelative \"\"\" wb = openpyxl.Workbook() # 创建工作簿 ws_china = wb.active # 获取工作表 ws_china.title = \"中国省份疫情数据\" # 命名工作表 ws_china.append(['省/直辖市/自治区/行政区', '现有确诊', '累计确诊', '累计治愈', '累计死亡', '现有确诊增量', '累计确诊增量', '累计治愈增量', '累计死亡增量']) china = data['component'][0]['caseList'] for province in china: ws_china.append([province['area'], province['curConfirm'], province['confirmed'], province['crued'], province['died'], province['curConfirmRelative'], province['confirmedRelative'], province['curedRelative'], province['diedRelative']]) \"\"\" 2、中国城市疫情数据 城市:city 现有确诊:curConfirm 累计确诊:confirmed 累计治愈:crued 累计死亡:died 累计确诊增量:confirmedRelative \"\"\" ws_city = wb.create_sheet('中国城市疫情数据') ws_city.append(['城市', '现有确诊', '累计确诊', '累计治愈', '累计死亡', '累计确诊增量']) for province in china: for city in province['subList']: # 某些城市没有 curConfirm 数据,则将其设置为 0,crued 和 died 为空时,替换成 0 if 'curConfirm' not in city: city['curConfirm'] = '0' if city['crued'] == '': city['crued'] = '0' if city['died'] == '': city['died'] = '0' ws_city.append([city['city'], '0', city['confirmed'], city['crued'], city['died'], city['confirmedRelative']]) \"\"\" 3、中国疫情数据更新时间:mapLastUpdatedTime \"\"\" time_domestic = data['component'][0]['mapLastUpdatedTime'] ws_time = wb.create_sheet('中国疫情数据更新时间') ws_time.column_dimensions['A'].width = 22 # 调整列宽 ws_time.append(['中国疫情数据更新时间']) ws_time.append([time_domestic]) wb.save('COVID-19-China.xlsx') print('中国疫情数据已保存至 COVID-19-China.xlsx!') 【5x03】全球总数据 global_total_data()全球总数据在提取完成后,进行地图绘制时发现并没有中国的数据,因此在写入全球数据时注意要单独将中国的数据插入 Excel 中。 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364def global_total_data(data): \"\"\" 1、全球各国疫情数据 国家:country 现有确诊:curConfirm 累计确诊:confirmed 累计治愈:crued 累计死亡:died 累计确诊增量:confirmedRelative \"\"\" wb = openpyxl.Workbook() ws_global = wb.active ws_global.title = \"全球各国疫情数据\" # 按照国家保存数据 countries = data['component'][0]['caseOutsideList'] ws_global.append(['国家', '现有确诊', '累计确诊', '累计治愈', '累计死亡', '累计确诊增量']) for country in countries: ws_global.append([country['area'], country['curConfirm'], country['confirmed'], country['crued'], country['died'], country['confirmedRelative']]) # 按照洲保存数据 continent = data['component'][0]['globalList'] for area in continent: ws_foreign = wb.create_sheet(area['area'] + '疫情数据') ws_foreign.append(['国家', '现有确诊', '累计确诊', '累计治愈', '累计死亡', '累计确诊增量']) for country in area['subList']: ws_foreign.append([country['country'], country['curConfirm'], country['confirmed'], country['crued'], country['died'], country['confirmedRelative']]) # 在“全球各国疫情数据”和“亚洲疫情数据”两张表中写入中国疫情数据 ws1, ws2 = wb['全球各国疫情数据'], wb['亚洲疫情数据'] original_data = data['component'][0]['summaryDataIn'] add_china_data = ['中国', original_data['curConfirm'], original_data['confirmed'], original_data['cured'], original_data['died'], original_data['confirmedRelative']] ws1.append(add_china_data) ws2.append(add_china_data) \"\"\" 2、全球疫情数据更新时间:foreignLastUpdatedTime \"\"\" time_foreign = data['component'][0]['foreignLastUpdatedTime'] ws_time = wb.create_sheet('全球疫情数据更新时间') ws_time.column_dimensions['A'].width = 22 # 调整列宽 ws_time.append(['全球疫情数据更新时间']) ws_time.append([time_foreign]) wb.save('COVID-19-Global.xlsx') print('全球疫情数据已保存至 COVID-19-Global.xlsx!') 【5x04】中国每日数据 china_daily_data()1234567891011121314151617181920212223242526272829303132333435363738394041424344def china_daily_data(data): \"\"\" i_dict = data['component'][0]['trend'] i_dict['updateDate']:日期 i_dict['list'][0]:确诊 i_dict['list'][1]:疑似 i_dict['list'][2]:治愈 i_dict['list'][3]:死亡 i_dict['list'][4]:新增确诊 i_dict['list'][5]:新增疑似 i_dict['list'][6]:新增治愈 i_dict['list'][7]:新增死亡 i_dict['list'][8]:累计境外输入 i_dict['list'][9]:新增境外输入 \"\"\" ccd_dict = data['component'][0]['trend'] update_date = ccd_dict['updateDate'] # 日期 china_confirmed = ccd_dict['list'][0]['data'] # 每日累计确诊数据 china_crued = ccd_dict['list'][2]['data'] # 每日累计治愈数据 china_died = ccd_dict['list'][3]['data'] # 每日累计死亡数据 wb = openpyxl.load_workbook('COVID-19-China.xlsx') # 写入每日累计确诊数据 ws_china_confirmed = wb.create_sheet('中国每日累计确诊数据') ws_china_confirmed.append(['日期', '数据']) for data in zip(update_date, china_confirmed): ws_china_confirmed.append(data) # 写入每日累计治愈数据 ws_china_crued = wb.create_sheet('中国每日累计治愈数据') ws_china_crued.append(['日期', '数据']) for data in zip(update_date, china_crued): ws_china_crued.append(data) # 写入每日累计死亡数据 ws_china_died = wb.create_sheet('中国每日累计死亡数据') ws_china_died.append(['日期', '数据']) for data in zip(update_date, china_died): ws_china_died.append(data) wb.save('COVID-19-China.xlsx') print('中国每日累计确诊/治愈/死亡数据已保存至 COVID-19-China.xlsx!') 【5x05】境外每日数据 foreign_daily_data()123456789101112131415161718192021222324252627282930313233343536373839def foreign_daily_data(data): \"\"\" te_dict = data['component'][0]['allForeignTrend'] te_dict['updateDate']:日期 te_dict['list'][0]:累计确诊 te_dict['list'][1]:治愈 te_dict['list'][2]:死亡 te_dict['list'][3]:现有确诊 te_dict['list'][4]:新增确诊 \"\"\" te_dict = data['component'][0]['allForeignTrend'] update_date = te_dict['updateDate'] # 日期 foreign_confirmed = te_dict['list'][0]['data'] # 每日累计确诊数据 foreign_crued = te_dict['list'][1]['data'] # 每日累计治愈数据 foreign_died = te_dict['list'][2]['data'] # 每日累计死亡数据 wb = openpyxl.load_workbook('COVID-19-Global.xlsx') # 写入每日累计确诊数据 ws_foreign_confirmed = wb.create_sheet('境外每日累计确诊数据') ws_foreign_confirmed.append(['日期', '数据']) for data in zip(update_date, foreign_confirmed): ws_foreign_confirmed.append(data) # 写入累计治愈数据 ws_foreign_crued = wb.create_sheet('境外每日累计治愈数据') ws_foreign_crued.append(['日期', '数据']) for data in zip(update_date, foreign_crued): ws_foreign_crued.append(data) # 写入累计死亡数据 ws_foreign_died = wb.create_sheet('境外每日累计死亡数据') ws_foreign_died.append(['日期', '数据']) for data in zip(update_date, foreign_died): ws_foreign_died.append(data) wb.save('COVID-19-Global.xlsx') print('境外每日累计确诊/治愈/死亡数据已保存至 COVID-19-Global.xlsx!') 【6x00】词云图绘制模块 data_wordcloud【6x01】中国累计确诊词云图 foreign_daily_data()1234567891011121314def china_wordcloud(): wb = openpyxl.load_workbook('COVID-19-China.xlsx') # 获取已有的xlsx文件 ws_china = wb['中国省份疫情数据'] # 获取中国省份疫情数据表 ws_china.delete_rows(1) # 删除第一行 china_dict = {} # 将省份及其累计确诊按照键值对形式储存在字典中 for data in ws_china.values: china_dict[data[0]] = int(data[2]) word_cloud = wordcloud.WordCloud(font_path='C:/Windows/Fonts/simsun.ttc', background_color='#CDC9C9', min_font_size=15, width=900, height=500) word_cloud.generate_from_frequencies(china_dict) word_cloud.to_file('WordCloud-China.png') print('中国省份疫情词云图绘制完毕!') 【6x02】全球累计确诊词云图 foreign_daily_data()12345678910111213def global_wordcloud(): wb = openpyxl.load_workbook('COVID-19-Global.xlsx') ws_global = wb['全球各国疫情数据'] ws_global.delete_rows(1) global_dict = {} for data in ws_global.values: global_dict[data[0]] = int(data[2]) word_cloud = wordcloud.WordCloud(font_path='C:/Windows/Fonts/simsun.ttc', background_color='#CDC9C9', width=900, height=500) word_cloud.generate_from_frequencies(global_dict) word_cloud.to_file('WordCloud-Global.png') print('全球各国疫情词云图绘制完毕!') 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/107140534未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【7x00】地图绘制模块 data_map【7x01】中国累计确诊地图 china_total_map()123456789101112131415161718192021222324252627282930313233def china_total_map(): wb = openpyxl.load_workbook('COVID-19-China.xlsx') # 获取已有的xlsx文件 ws_time = wb['中国疫情数据更新时间'] # 获取文件中中国疫情数据更新时间表 ws_data = wb['中国省份疫情数据'] # 获取文件中中国省份疫情数据表 ws_data.delete_rows(1) # 删除第一行 province = [] # 省份 curconfirm = [] # 累计确诊 for data in ws_data.values: province.append(data[0]) curconfirm.append(data[2]) time_china = ws_time['A2'].value # 更新时间 # 设置分级颜色 pieces = [ {'max': 0, 'min': 0, 'label': '0', 'color': '#FFFFFF'}, {'max': 9, 'min': 1, 'label': '1-9', 'color': '#FFE5DB'}, {'max': 99, 'min': 10, 'label': '10-99', 'color': '#FF9985'}, {'max': 999, 'min': 100, 'label': '100-999', 'color': '#F57567'}, {'max': 9999, 'min': 1000, 'label': '1000-9999', 'color': '#E64546'}, {'max': 99999, 'min': 10000, 'label': '≧10000', 'color': '#B80909'} ] # 绘制地图 ct_map = ( Map() .add(series_name='累计确诊人数', data_pair=[list(z) for z in zip(province, curconfirm)], maptype=\"china\") .set_global_opts( title_opts=opts.TitleOpts(title=\"中国疫情数据(累计确诊)\", subtitle='数据更新至:' + time_china + '\\n\\n来源:百度疫情实时大数据报告'), visualmap_opts=opts.VisualMapOpts(max_=300, is_piecewise=True, pieces=pieces) ) ) return ct_map 【7x02】全球累计确诊地图 global_total_map()123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253def global_total_map(): wb = openpyxl.load_workbook('COVID-19-Global.xlsx') ws_time = wb['全球疫情数据更新时间'] ws_data = wb['全球各国疫情数据'] ws_data.delete_rows(1) country = [] # 国家 curconfirm = [] # 累计确诊 for data in ws_data.values: country.append(data[0]) curconfirm.append(data[2]) time_global = ws_time['A2'].value # 更新时间 # 国家名称中英文映射表 name_map = { \"Somalia\": \"索马里\", \"Liechtenstein\": \"列支敦士登\", \"Morocco\": \"摩洛哥\", \"W. Sahara\": \"西撒哈拉\", \"Serbia\": \"塞尔维亚\", \"Afghanistan\": \"阿富汗\", \"Angola\": \"安哥拉\", \"Albania\": \"阿尔巴尼亚\", \"Andorra\": \"安道尔共和国\", \"United Arab Emirates\": \"阿拉伯联合酋长国\", \"Argentina\": \"阿根廷\", \"Armenia\": \"亚美尼亚\", \"Australia\": \"澳大利亚\", \"Austria\": \"奥地利\", \"Azerbaijan\": \"阿塞拜疆\", \"Burundi\": \"布隆迪\", \"Belgium\": \"比利时\", \"Benin\": \"贝宁\", \"Burkina Faso\": \"布基纳法索\", \"Bangladesh\": \"孟加拉国\", \"Bulgaria\": \"保加利亚\", \"Bahrain\": \"巴林\", \"Bahamas\": \"巴哈马\", \"Bosnia and Herz.\": \"波斯尼亚和黑塞哥维那\", \"Belarus\": \"白俄罗斯\", \"Belize\": \"伯利兹\", \"Bermuda\": \"百慕大\", \"Bolivia\": \"玻利维亚\", \"Brazil\": \"巴西\", \"Barbados\": \"巴巴多斯\", \"Brunei\": \"文莱\", \"Bhutan\": \"不丹\", \"Botswana\": \"博茨瓦纳\", \"Central African Rep.\": \"中非共和国\", \"Canada\": \"加拿大\", \"Switzerland\": \"瑞士\", \"Chile\": \"智利\", \"China\": \"中国\", \"Côte d'Ivoire\": \"科特迪瓦\", \"Cameroon\": \"喀麦隆\", \"Dem. Rep. Congo\": \"刚果(布)\", \"Congo\": \"刚果(金)\", \"Colombia\": \"哥伦比亚\", \"Cape Verde\": \"佛得角\", \"Costa Rica\": \"哥斯达黎加\", \"Cuba\": \"古巴\", \"N. Cyprus\": \"北塞浦路斯\", \"Cyprus\": \"塞浦路斯\", \"Czech Rep.\": \"捷克\", \"Germany\": \"德国\", \"Djibouti\": \"吉布提\", \"Denmark\": \"丹麦\", \"Dominican Rep.\": \"多米尼加\", \"Algeria\": \"阿尔及利亚\", \"Ecuador\": \"厄瓜多尔\", \"Egypt\": \"埃及\", \"Eritrea\": \"厄立特里亚\", \"Spain\": \"西班牙\", \"Estonia\": \"爱沙尼亚\", \"Ethiopia\": \"埃塞俄比亚\", \"Finland\": \"芬兰\", \"Fiji\": \"斐济\", \"France\": \"法国\", \"Gabon\": \"加蓬\", \"United Kingdom\": \"英国\", \"Georgia\": \"格鲁吉亚\", \"Ghana\": \"加纳\", \"Guinea\": \"几内亚\", \"Gambia\": \"冈比亚\", \"Guinea-Bissau\": \"几内亚比绍\", \"Eq. Guinea\": \"赤道几内亚\", \"Greece\": \"希腊\", \"Grenada\": \"格林纳达\", \"Greenland\": \"格陵兰岛\", \"Guatemala\": \"危地马拉\", \"Guam\": \"关岛\", \"Guyana\": \"圭亚那合作共和国\", \"Honduras\": \"洪都拉斯\", \"Croatia\": \"克罗地亚\", \"Haiti\": \"海地\", \"Hungary\": \"匈牙利\", \"Indonesia\": \"印度尼西亚\", \"India\": \"印度\", \"Br. Indian Ocean Ter.\": \"英属印度洋领土\", \"Ireland\": \"爱尔兰\", \"Iran\": \"伊朗\", \"Iraq\": \"伊拉克\", \"Iceland\": \"冰岛\", \"Israel\": \"以色列\", \"Italy\": \"意大利\", \"Jamaica\": \"牙买加\", \"Jordan\": \"约旦\", \"Japan\": \"日本\", \"Siachen Glacier\": \"锡亚琴冰川\", \"Kazakhstan\": \"哈萨克斯坦\", \"Kenya\": \"肯尼亚\", \"Kyrgyzstan\": \"吉尔吉斯斯坦\", \"Cambodia\": \"柬埔寨\", \"Korea\": \"韩国\", \"Kuwait\": \"科威特\", \"Lao PDR\": \"老挝\", \"Lebanon\": \"黎巴嫩\", \"Liberia\": \"利比里亚\", \"Libya\": \"利比亚\", \"Sri Lanka\": \"斯里兰卡\", \"Lesotho\": \"莱索托\", \"Lithuania\": \"立陶宛\", \"Luxembourg\": \"卢森堡\", \"Latvia\": \"拉脱维亚\", \"Moldova\": \"摩尔多瓦\", \"Madagascar\": \"马达加斯加\", \"Mexico\": \"墨西哥\", \"Macedonia\": \"马其顿\", \"Mali\": \"马里\", \"Malta\": \"马耳他\", \"Myanmar\": \"缅甸\", \"Montenegro\": \"黑山\", \"Mongolia\": \"蒙古国\", \"Mozambique\": \"莫桑比克\", \"Mauritania\": \"毛里塔尼亚\", \"Mauritius\": \"毛里求斯\", \"Malawi\": \"马拉维\", \"Malaysia\": \"马来西亚\", \"Namibia\": \"纳米比亚\", \"New Caledonia\": \"新喀里多尼亚\", \"Niger\": \"尼日尔\", \"Nigeria\": \"尼日利亚\", \"Nicaragua\": \"尼加拉瓜\", \"Netherlands\": \"荷兰\", \"Norway\": \"挪威\", \"Nepal\": \"尼泊尔\", \"New Zealand\": \"新西兰\", \"Oman\": \"阿曼\", \"Pakistan\": \"巴基斯坦\", \"Panama\": \"巴拿马\", \"Peru\": \"秘鲁\", \"Philippines\": \"菲律宾\", \"Papua New Guinea\": \"巴布亚新几内亚\", \"Poland\": \"波兰\", \"Puerto Rico\": \"波多黎各\", \"Dem. Rep. Korea\": \"朝鲜\", \"Portugal\": \"葡萄牙\", \"Paraguay\": \"巴拉圭\", \"Palestine\": \"巴勒斯坦\", \"Qatar\": \"卡塔尔\", \"Romania\": \"罗马尼亚\", \"Russia\": \"俄罗斯\", \"Rwanda\": \"卢旺达\", \"Saudi Arabia\": \"沙特阿拉伯\", \"Sudan\": \"苏丹\", \"S. Sudan\": \"南苏丹\", \"Senegal\": \"塞内加尔\", \"Singapore\": \"新加坡\", \"Solomon Is.\": \"所罗门群岛\", \"Sierra Leone\": \"塞拉利昂\", \"El Salvador\": \"萨尔瓦多\", \"Suriname\": \"苏里南\", \"Slovakia\": \"斯洛伐克\", \"Slovenia\": \"斯洛文尼亚\", \"Sweden\": \"瑞典\", \"Swaziland\": \"斯威士兰\", \"Seychelles\": \"塞舌尔\", \"Syria\": \"叙利亚\", \"Chad\": \"乍得\", \"Togo\": \"多哥\", \"Thailand\": \"泰国\", \"Tajikistan\": \"塔吉克斯坦\", \"Turkmenistan\": \"土库曼斯坦\", \"Timor-Leste\": \"东帝汶\", \"Tonga\": \"汤加\", \"Trinidad and Tobago\": \"特立尼达和多巴哥\", \"Tunisia\": \"突尼斯\", \"Turkey\": \"土耳其\", \"Tanzania\": \"坦桑尼亚\", \"Uganda\": \"乌干达\", \"Ukraine\": \"乌克兰\", \"Uruguay\": \"乌拉圭\", \"United States\": \"美国\", \"Uzbekistan\": \"乌兹别克斯坦\", \"Venezuela\": \"委内瑞拉\", \"Vietnam\": \"越南\", \"Vanuatu\": \"瓦努阿图\", \"Yemen\": \"也门\", \"South Africa\": \"南非\", \"Zambia\": \"赞比亚\", \"Zimbabwe\": \"津巴布韦\", \"Aland\": \"奥兰群岛\", \"American Samoa\": \"美属萨摩亚\", \"Fr. S. Antarctic Lands\": \"南极洲\", \"Antigua and Barb.\": \"安提瓜和巴布达\", \"Comoros\": \"科摩罗\", \"Curaçao\": \"库拉索岛\", \"Cayman Is.\": \"开曼群岛\", \"Dominica\": \"多米尼加\", \"Falkland Is.\": \"福克兰群岛马尔维纳斯\", \"Faeroe Is.\": \"法罗群岛\", \"Micronesia\": \"密克罗尼西亚\", \"Heard I. and McDonald Is.\": \"赫德岛和麦克唐纳群岛\", \"Isle of Man\": \"曼岛\", \"Jersey\": \"泽西岛\", \"Kiribati\": \"基里巴斯\", \"Saint Lucia\": \"圣卢西亚\", \"N. Mariana Is.\": \"北马里亚纳群岛\", \"Montserrat\": \"蒙特塞拉特\", \"Niue\": \"纽埃\", \"Palau\": \"帕劳\", \"Fr. Polynesia\": \"法属波利尼西亚\", \"S. Geo. and S. Sandw. Is.\": \"南乔治亚岛和南桑威奇群岛\", \"Saint Helena\": \"圣赫勒拿\", \"St. Pierre and Miquelon\": \"圣皮埃尔和密克隆群岛\", \"São Tomé and Principe\": \"圣多美和普林西比\", \"Turks and Caicos Is.\": \"特克斯和凯科斯群岛\", \"St. Vin. and Gren.\": \"圣文森特和格林纳丁斯\", \"U.S. Virgin Is.\": \"美属维尔京群岛\", \"Samoa\": \"萨摩亚\" } pieces = [ {'max': 0, 'min': 0, 'label': '0', 'color': '#FFFFFF'}, {'max': 49, 'min': 1, 'label': '1-49', 'color': '#FFE5DB'}, {'max': 99, 'min': 50, 'label': '50-99', 'color': '#FFC4B3'}, {'max': 999, 'min': 100, 'label': '100-999', 'color': '#FF9985'}, {'max': 9999, 'min': 1000, 'label': '1000-9999', 'color': '#F57567'}, {'max': 99999, 'min': 10000, 'label': '10000-99999', 'color': '#E64546'}, {'max': 999999, 'min': 100000, 'label': '100000-999999', 'color': '#B80909'}, {'max': 9999999, 'min': 1000000, 'label': '≧1000000', 'color': '#8A0808'} ] gt_map = ( Map() .add(series_name='累计确诊人数', data_pair=[list(z) for z in zip(country, curconfirm)], maptype=\"world\", name_map=name_map, is_map_symbol_show=False) .set_series_opts(label_opts=opts.LabelOpts(is_show=False)) .set_global_opts( title_opts=opts.TitleOpts(title=\"全球疫情数据(累计确诊)\", subtitle='数据更新至:' + time_global + '\\n\\n来源:百度疫情实时大数据报告'), visualmap_opts=opts.VisualMapOpts(max_=300, is_piecewise=True, pieces=pieces), ) ) return gt_map 【7x03】中国每日数据折线图 china_daily_map()123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354def china_daily_map(): wb = openpyxl.load_workbook('COVID-19-China.xlsx') ws_china_confirmed = wb['中国每日累计确诊数据'] ws_china_crued = wb['中国每日累计治愈数据'] ws_china_died = wb['中国每日累计死亡数据'] ws_china_confirmed.delete_rows(1) ws_china_crued.delete_rows(1) ws_china_died.delete_rows(1) x_date = [] # 日期 y_china_confirmed = [] # 每日累计确诊 y_china_crued = [] # 每日累计治愈 y_china_died = [] # 每日累计死亡 for china_confirmed in ws_china_confirmed.values: y_china_confirmed.append(china_confirmed[1]) for china_crued in ws_china_crued.values: x_date.append(china_crued[0]) y_china_crued.append(china_crued[1]) for china_died in ws_china_died.values: y_china_died.append(china_died[1]) fi_map = ( Line(init_opts=opts.InitOpts(height='420px')) .add_xaxis(xaxis_data=x_date) .add_yaxis( series_name=\"中国累计确诊数据\", y_axis=y_china_confirmed, label_opts=opts.LabelOpts(is_show=False), ) .add_yaxis( series_name=\"中国累计治愈趋势\", y_axis=y_china_crued, label_opts=opts.LabelOpts(is_show=False), ) .add_yaxis( series_name=\"中国累计死亡趋势\", y_axis=y_china_died, label_opts=opts.LabelOpts(is_show=False), ) .set_global_opts( title_opts=opts.TitleOpts(title=\"中国每日累计确诊/治愈/死亡趋势\"), legend_opts=opts.LegendOpts(pos_bottom=\"bottom\", orient='horizontal'), tooltip_opts=opts.TooltipOpts(trigger=\"axis\"), yaxis_opts=opts.AxisOpts( type_=\"value\", axistick_opts=opts.AxisTickOpts(is_show=True), splitline_opts=opts.SplitLineOpts(is_show=True), ), xaxis_opts=opts.AxisOpts(type_=\"category\", boundary_gap=False), ) ) return fi_map 【7x04】境外每日数据折线图 foreign_daily_map()123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354def foreign_daily_map(): wb = openpyxl.load_workbook('COVID-19-Global.xlsx') ws_foreign_confirmed = wb['境外每日累计确诊数据'] ws_foreign_crued = wb['境外每日累计治愈数据'] ws_foreign_died = wb['境外每日累计死亡数据'] ws_foreign_confirmed.delete_rows(1) ws_foreign_crued.delete_rows(1) ws_foreign_died.delete_rows(1) x_date = [] # 日期 y_foreign_confirmed = [] # 累计确诊 y_foreign_crued = [] # 累计治愈 y_foreign_died = [] # 累计死亡 for foreign_confirmed in ws_foreign_confirmed.values: y_foreign_confirmed.append(foreign_confirmed[1]) for foreign_crued in ws_foreign_crued.values: x_date.append(foreign_crued[0]) y_foreign_crued.append(foreign_crued[1]) for foreign_died in ws_foreign_died.values: y_foreign_died.append(foreign_died[1]) fte_map = ( Line(init_opts=opts.InitOpts(height='420px')) .add_xaxis(xaxis_data=x_date) .add_yaxis( series_name=\"境外累计确诊趋势\", y_axis=y_foreign_confirmed, label_opts=opts.LabelOpts(is_show=False), ) .add_yaxis( series_name=\"境外累计治愈趋势\", y_axis=y_foreign_crued, label_opts=opts.LabelOpts(is_show=False), ) .add_yaxis( series_name=\"境外累计死亡趋势\", y_axis=y_foreign_died, label_opts=opts.LabelOpts(is_show=False), ) .set_global_opts( title_opts=opts.TitleOpts(title=\"境外每日累计确诊/治愈/死亡趋势\"), legend_opts=opts.LegendOpts(pos_bottom=\"bottom\", orient='horizontal'), tooltip_opts=opts.TooltipOpts(trigger=\"axis\"), yaxis_opts=opts.AxisOpts( type_=\"value\", axistick_opts=opts.AxisTickOpts(is_show=True), splitline_opts=opts.SplitLineOpts(is_show=True), ), xaxis_opts=opts.AxisOpts(type_=\"category\", boundary_gap=False), ) ) return fte_map 【8x00】结果截图【8x01】数据储存 Excel 【8x02】词云图 【8x03】地图 + 折线图 【9x00】完整代码预览地址:http://cov.itrhx.com/ 完整代码地址(点亮 star 有 buff 加成):https://github.com/TRHX/Python3-Spider-Practice/tree/master/COVID-19 其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice 爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/107140534未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫实战","slug":"Python3-学习笔记/爬虫实战","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫实战/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"肺炎疫情","slug":"肺炎疫情","permalink":"https://www.itrhx.com/tags/肺炎疫情/"}]},{"title":"Python 数据分析三剑客之 Pandas(十):数据读写","slug":"A88-Pandas-10","date":"2020-06-26T15:13:58.975Z","updated":"2020-08-06T03:33:37.232Z","comments":true,"path":"2020/06/26/A88-Pandas-10/","link":"","permalink":"https://www.itrhx.com/2020/06/26/A88-Pandas-10/","excerpt":"","text":"Pandas 系列文章: Python 数据分析三剑客之 Pandas(一):认识 Pandas 及其 Series、DataFrame 对象 Python 数据分析三剑客之 Pandas(二):Index 索引对象以及各种索引操作 Python 数据分析三剑客之 Pandas(三):算术运算与缺失值的处理 Python 数据分析三剑客之 Pandas(四):函数应用、映射、排序和层级索引 Python 数据分析三剑客之 Pandas(五):统计计算与统计描述 Python 数据分析三剑客之 Pandas(六):GroupBy 数据分裂、应用与合并 Python 数据分析三剑客之 Pandas(七):合并数据集 Python 数据分析三剑客之 Pandas(八):数据重塑、重复数据处理与数据替换 Python 数据分析三剑客之 Pandas(九):时间序列 Python 数据分析三剑客之 Pandas(十):数据读写 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106963135未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【01x00】读取数据Pandas 提供了一些用于将表格型数据读取为 DataFrame 对象的函数。常见方法如下: Pandas 官方对 IO 工具的介绍:https://pandas.pydata.org/pandas-docs/stable/user_guide/io.html 函数 描述 read_csv 从文件、URL、文件型对象中加载带分隔符的数据。默认分隔符为逗号 read_table 从文件、URL、文件型对象中加载带分隔符的数据。默认分隔符为制表符('\\t') read_fwf 读取定宽列格式数据(没有分隔符) read_clipboard 读取剪贴板中的数据,可以看做 read_table 的剪贴板版本。在将网页转换为表格时很有用 read_excel 从 Excel XLS 或 XLSX file 读取表格数据 read_hdf 读取 pandas写的 HDF5 文件 read_html 读取 HTML 文档中的所有表格 read_json 读取 JSON( JavaScript Object Notation)字符串中的数据 read_msgpack 读取二进制格式编码的 pandas 数据(Pandas v1.0.0 中已删除对 msgpack 的支持,建议使用 pyarrow) read_pickle 读取 Python pickle 格式中存储的任意对象 read_sas 读取存储于 SAS 系统自定义存储格式的 SAS 数据集 read_sql (使用 SQLAlchemy)读取 SQL 查询结果为 pandas 的 DataFrame read_stata 读取 Stata 文件格式的数据集 read_feather 读取 Feather 二进制格式文件 以下以 read_csv 和 read_table 为例,它们的参数多达 50 多个,具体可参见官方文档: read_csv:https://pandas.pydata.org/docs/reference/api/pandas.read_csv.html read_table:https://pandas.pydata.org/docs/reference/api/pandas.read_table.html 常用参数: 参数 描述 path 表示文件系统位置、URL、文件型对象的字符串 sep / delimiter 用于对行中各字段进行拆分的字符序列或正则表达式 header 用作列名的行号,默认为 0(第一行),如果没有 header 行就应该设置为 None index_col 用作行索引的列编号或列名。可以是单个名称、数字或由多个名称、数字组成的列表(层次化索引) names 用于结果的列名列表,结合 header=None skiprows 需要忽略的行数(从文件开始处算起),或需要跳过的行号列表(从0开始) na_values 指定一组值,将该组值设置为 NaN(缺失值) comment 用于将注释信息从行尾拆分出去的字符(一个或多个) parse_dates 尝试将数据解析为日期,默认为 False。如果为 True,则尝试解析所有列。此外,还可以指定需要解析的一组列号或列名。如果列表的元素为列表或元组,就会将多个列组合到一起再进行日期解析工作(例如,日期、时间分别位于两个列中) keep_date_col 如果连接多列解析日期,则保持参与连接的列。默认为 False converters 由列号 / 列名跟函数之间的映射关系组成的字典。例如,{'foo': f} 会对 foo 列的所有值应用函数 f dayfirst 当解析有歧义的日期时,将其看做国际格式(例如,7/6/2012 —> June 7,2012),默认为 Fase date_parser 用于解析日期的函数 nrows 需要读取的行数(从文件开始处算起) iterator 返回一个 TextParser 以便逐块读取文件 chunksize 文件块的大小(用于迭代) skip_footer 需要忽略的行数(从文件末尾处算起) verbose 打印各种解析器输出信息,比如“非数值列中缺失值的数量”等 encoding 用于 unicode 的文本编码格式。例如,“utf-8” 表示用 UTF-8 编码的文本 squeeze 如果数据经解析后仅含一列,则返回 Series thousands 千分位分隔符,如 , 或 . 【01x01】简单示例首先创建一个 test1.csv 文件: 使用 read_csv 方法将其读出为一个 DataFrame 对象: 12345678910>>> import pandas as pd>>> obj = pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test1.csv')>>> obj a b c d message0 1 2 3 4 hello1 5 6 7 8 world2 9 10 11 12 python>>> >>> type(obj)<class 'pandas.core.frame.DataFrame'> 前面的 csv 文件是以逗号分隔的,可以使用 read_table 方法并指定分隔符来读取: 1234567>>> import pandas as pd>>> obj = pd.read_table(r'C:\\Users\\TanRe\\Desktop\\test1.csv', sep=',')>>> obj a b c d message0 1 2 3 4 hello1 5 6 7 8 world2 9 10 11 12 python 【01x02】header / names 定制列标签以上示例中第一行为列标签,如果没有单独定义列标签,使用 read_csv 方法也会默认将第一行当作列标签: 123456>>> import pandas as pd>>> obj = pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test2.csv')>>> obj 1 2 3 4 hello0 5 6 7 8 world1 9 10 11 12 python 避免以上情况,可以设置 header=None,Pandas 会为其自动分配列标签: 123456>>> import pandas as pd>>> pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test2.csv', header=None) 0 1 2 3 40 1 2 3 4 hello1 5 6 7 8 world2 9 10 11 12 python 也可以使用 names 参数自定义列标签,传递的是一个列表: 123456>>> import pandas as pd>>> pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test2.csv', names=['a', 'b', 'c', 'd', 'message']) a b c d message0 1 2 3 4 hello1 5 6 7 8 world2 9 10 11 12 python 【01x03】index_col 指定列为行索引index_col 参数可以指定某一列作为 DataFrame 的行索引,传递的参数是列名称,在以下示例中,会将列名为 message 的列作为 DataFrame 的行索引: 12345678>>> pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test2.csv', names=['a', 'b', 'c', 'd', 'message'], index_col='message') a b c dmessage hello 1 2 3 4world 5 6 7 8python 9 10 11 12 如果需要构造多层索引的 DataFrame 对象,则只需传入由列编号或列名组成的列表即可: 123456789101112>>> import pandas as pd>>> pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test3.csv', index_col=['key1', 'key2']) value1 value2key1 key2 one a 1 2 b 3 4 c 5 6 d 7 8two a 9 10 b 11 12 c 13 14 d 15 16 【01x04】sep 指定分隔符在 read_table 中,sep 参数用于接收分隔符,如果遇到不是用固定的分隔符去分隔字段的,也可以传递一个正则表达式作为 read_table 的分隔符,如下面的 txt 文件数据之间是由不同的空白字符间隔开的: 1234567>>> import pandas as pd>>> pd.read_table(r'C:\\Users\\TanRe\\Desktop\\test1.txt', sep='\\s+') A B Caaa -0.264438 -1.026059 -0.619500bbb 0.927272 0.302904 -0.032399ccc -0.264273 -0.386314 -0.217601ddd -0.871858 -0.348382 1.100491 【01x05】skiprows 忽略行skiprows 参数可用于设置需要忽略的行数,或需要跳过的行号列表,在下面的示例中,读取文件时选择跳过第1、3、4行(索引值分别为0、2、3): 123456>>> import pandas as pd>>> pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test4.csv', skiprows=[0, 2, 3]) a b c d message0 1 2 3 4 hello1 5 6 7 8 world2 9 10 11 12 python 【01x06】na_values 设置缺失值当文件中出现了空字符串或者 NA 值,Pandas 会将其标记成 NaN(缺失值),同样也可以使用 isnull 方法来判断结果值是否为缺失值: 12345678910111213>>> import pandas as pd>>> obj = pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test5.csv')>>> obj something a b c d message0 one 1 2 3.0 4 NaN1 two 5 6 NaN 8 world2 three 9 10 11.0 12 python>>> >>> pd.isnull(obj) something a b c d message0 False False False False False True1 False False False True False False2 False False False False False False na_values 方法可以传递一组值,将这组值设置为缺失值,如果传递的为字典对象,则字典的各值将被设置为 NaN: 12345678910111213141516171819202122>>> import pandas as pd>>> obj1 = pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test5.csv')>>> obj1 something a b c d message0 one 1 2 3.0 4 NaN1 two 5 6 NaN 8 world2 three 9 10 11.0 12 python>>> >>> obj2 = pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test5.csv', na_values=['1', '12'])>>> obj2 something a b c d message0 one NaN 2 3.0 4.0 NaN1 two 5.0 6 NaN 8.0 world2 three 9.0 10 11.0 NaN python>>> >>> sentinels = {'message': ['python', 'world'], 'something': ['two']}>>> obj3 = pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test5.csv', na_values=sentinels)>>> obj3 something a b c d message0 one 1 2 3.0 4 NaN1 NaN 5 6 NaN 8 NaN2 three 9 10 11.0 12 NaN 【01x07】nrows / chunksize 行与块以下 test6.csv 文件中包含 50 行数据: 可以设置 pd.options.display.max_rows 来紧凑地显示指定行数的数据: 1234567891011121314151617>>> import pandas as pd>>> pd.options.display.max_rows = 10>>> pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test6.csv') one two three four key0 0.467976 -0.038649 -0.295344 -1.824726 L1 -0.358893 1.404453 0.704965 -0.200638 B2 -0.501840 0.659254 -0.421691 -0.057688 G3 0.204886 1.074134 1.388361 -0.982404 R4 0.354628 -0.133116 0.283763 -0.837063 Q.. ... ... ... ... ..45 2.311896 -0.417070 -1.409599 -0.515821 L46 -0.479893 -0.633419 0.745152 -0.646038 E47 0.523331 0.787112 0.486066 1.093156 K48 -0.362559 0.598894 -1.843201 0.887292 G49 -0.096376 -1.012999 -0.657431 -0.573315 0[50 rows x 5 columns] 通过 nrows 参数可以读取指定行数: 12345678>>> import pandas as pd>>> pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test6.csv', nrows=5) one two three four key0 0.467976 -0.038649 -0.295344 -1.824726 L1 -0.358893 1.404453 0.704965 -0.200638 B2 -0.501840 0.659254 -0.421691 -0.057688 G3 0.204886 1.074134 1.388361 -0.982404 R4 0.354628 -0.133116 0.283763 -0.837063 Q 要逐块读取文件,可以指定 chunksize(行数): 1234>>> import pandas as pd>>> chunker = pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test6.csv', chunksize=50)>>> chunker<pandas.io.parsers.TextFileReader object at 0x07A20D60> 返回的 TextParser 对象,可以根据 chunksize 对文件进行逐块迭代。以下示例中,对 test6.csv 文件数据进行迭代处理,将值计数聚合到 “key” 列中: 1234567891011121314151617181920>>> import pandas as pd>>> chunker = pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test6.csv', chunksize=50)>>> tot = pd.Series([], dtype='float64')>>> for piece in chunker: tot = tot.add(piece['key'].value_counts(), fill_value=0)>>> tot = tot.sort_values(ascending=False)>>> tot[:10]G 6.0E 5.0B 5.0L 5.00 5.0K 4.0A 4.0R 4.0C 2.0Q 2.0dtype: float64 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106963135未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【02x00】写入数据Pandas 提供了一些用于将表格型数据读取为 DataFrame 对象的函数。常见方法如下: 函数 描述 to_csv 将对象写入逗号分隔值(csv)文件 to_clipboard 将对象复制到系统剪贴板 to_excel 将对象写入 Excel 工作表 to_hdf 使用 HDFStore 将包含的数据写入 HDF5 文件 to_html 将 DataFrame 呈现为 HTML 表格 to_json 将对象转换为 JSON( JavaScript Object Notation)字符串 to_msgpack 将对象写入二进制格式编码的文件(Pandas v1.0.0 中已删除对 msgpack 的支持,建议使用 pyarrow) to_pickle Pickle(序列化)对象到文件 to_sql 将存储在 DataFrame 中的数据写入 SQL 数据库 to_stata 将 DataFrame 对象导出为 Stata 格式 to_feather 将 DataFrames 写入 Feather 二进制格式文件 以下以 to_csv 为例,它的参数同样多达 50 多个,具体可参见官方文档: https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.to_csv.html https://pandas.pydata.org/docs/reference/api/pandas.Series.to_csv.html 【02x01】简单示例以之前的 test5.csv 文件为例,先读出数据,再将数据写入另外的文件: 123456789>>> import pandas as pd>>> data = pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test5.csv')>>> data something a b c d message0 one 1 2 3.0 4 NaN1 two 5 6 NaN 8 world2 three 9 10 11.0 12 python>>> >>> data.to_csv(r'C:\\Users\\TanRe\\Desktop\\out1.csv') 【02x02】sep 指定分隔符sep 参数可用于其他分隔符: 123456789>>> import pandas as pd>>> data = pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test5.csv')>>> data something a b c d message0 one 1 2 3.0 4 NaN1 two 5 6 NaN 8 world2 three 9 10 11.0 12 python>>>>>> data.to_csv(r'C:\\Users\\TanRe\\Desktop\\out2.csv', sep='|') 【02x03】na_rep 替换缺失值na_rep 参数可将缺失值(NaN)替换成其他字符串: 123456789>>> import pandas as pd>>> data = pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test5.csv')>>> data something a b c d message0 one 1 2 3.0 4 NaN1 two 5 6 NaN 8 world2 three 9 10 11.0 12 python>>> >>> data.to_csv(r'C:\\Users\\TanRe\\Desktop\\out3.csv', na_rep='X') 【02x04】index / header 行与列标签设置 index=False, header=False,可以禁用行标签与列标签: 123456789>>> import pandas as pd>>> data = pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test5.csv')>>> data something a b c d message0 one 1 2 3.0 4 NaN1 two 5 6 NaN 8 world2 three 9 10 11.0 12 python>>> >>> data.to_csv(r'C:\\Users\\TanRe\\Desktop\\out4.csv', index=False, header=False) 还可以传入列表来重新设置列标签: 123456789>>> import pandas as pd>>> data = pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test5.csv')>>> data something a b c d message0 one 1 2 3.0 4 NaN1 two 5 6 NaN 8 world2 three 9 10 11.0 12 python>>> >>> data.to_csv(r'C:\\Users\\TanRe\\Desktop\\out5.csv', header=['a', 'b', 'c', 'd', 'e', 'f']) 【02x05】columns 指定列可以通过设置 columns 参数,只写入部分列,并按照指定顺序排序: 123456789>>> import pandas as pd>>> data = pd.read_csv(r'C:\\Users\\TanRe\\Desktop\\test5.csv')>>> data something a b c d message0 one 1 2 3.0 4 NaN1 two 5 6 NaN 8 world2 three 9 10 11.0 12 python>>>>>> data.to_csv(r'C:\\Users\\TanRe\\Desktop\\out6.csv', columns=['c', 'b', 'a']) 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106963135未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Pandas","slug":"Python-数据分析/Pandas","permalink":"https://www.itrhx.com/categories/Python-数据分析/Pandas/"}],"tags":[{"name":"IO操作","slug":"IO操作","permalink":"https://www.itrhx.com/tags/IO操作/"},{"name":"Pandas","slug":"Pandas","permalink":"https://www.itrhx.com/tags/Pandas/"},{"name":"数据读写","slug":"数据读写","permalink":"https://www.itrhx.com/tags/数据读写/"}]},{"title":"Python 数据分析三剑客之 Pandas(九):时间序列","slug":"A87-Pandas-09","date":"2020-06-25T14:03:28.198Z","updated":"2020-07-06T13:45:31.263Z","comments":true,"path":"2020/06/25/A87-Pandas-09/","link":"","permalink":"https://www.itrhx.com/2020/06/25/A87-Pandas-09/","excerpt":"","text":"Pandas 系列文章: Python 数据分析三剑客之 Pandas(一):认识 Pandas 及其 Series、DataFrame 对象 Python 数据分析三剑客之 Pandas(二):Index 索引对象以及各种索引操作 Python 数据分析三剑客之 Pandas(三):算术运算与缺失值的处理 Python 数据分析三剑客之 Pandas(四):函数应用、映射、排序和层级索引 Python 数据分析三剑客之 Pandas(五):统计计算与统计描述 Python 数据分析三剑客之 Pandas(六):GroupBy 数据分裂、应用与合并 Python 数据分析三剑客之 Pandas(七):合并数据集 Python 数据分析三剑客之 Pandas(八):数据重塑、重复数据处理与数据替换 Python 数据分析三剑客之 Pandas(九):时间序列 Python 数据分析三剑客之 Pandas(十):数据读写 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106947061未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【01x00】时间序列官网对于时间序列的介绍:https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html 时间序列(time series)是一种重要的结构化数据形式,应用于多个领域,包括金融学、经济学、生态学、神经科学、物理学等。在多个时间点观察或测量到的任何事物都可以形成一段时间序列。很多时间序列是固定频率的,也就是说,数据点是根据某种规律定期出现的(比如每15秒、每5分钟、每月出现一次)。时间序列也可以是不定期的,没有固定的时间单位或单位之间的偏移量。时间序列数据的意义取决于具体的应用场景,主要有以下几种: 时间戳(timestamp),表示某个具体的时间点,例如 2020-6-24 15:30; 固定周期(period),表示某个时间周期,例如 2020-01; 时间间隔(timedelta),持续时间,即两个日期或时间之间的差异。 针对时间戳数据,Pandas 提供了 Timestamp 类型。它本质上是 Python 的原生 datetime 类型的替代品,但是在性能更好的 numpy.datetime64 类型的基础上创建。对应的索引数据结构是 DatetimeIndex。 针对时间周期数据,Pandas 提供了 Period 类型。这是利用 numpy.datetime64 类型将固定频率的时间间隔进行编码。对应的索引数据结构是 PeriodIndex。 针对时间增量或持续时间,Pandas 提供了 Timedelta 类型。Timedelta 是一种代替 Python 原生datetime.timedelta 类型的高性能数据结构,同样是基于 numpy.timedelta64 类型。对应的索引数据结构是 TimedeltaIndex。 【02x00】Timestamp 时间戳【02x01】pandas.Timestamp在 pandas 中,pandas.Timestamp 方法用来代替 Python 中的 datetime.datetime 方法。 Timestamp 与 Python 的 Datetime 等效,在大多数情况下都可以互换。 此类型用于组成 DatetimeIndex 以及 Pandas 中其他面向时间序列的数据结构。 官方文档:https://pandas.pydata.org/docs/reference/api/pandas.Timestamp.html 基本语法: 12345class pandas.Timestamp(ts_input=<object object>, freq=None, tz=None, unit=None, year=None, month=None, day=None, hour=None, minute=None, second=None, microsecond=None, nanosecond=None, tzinfo=None) 常用参数: 参数 描述 ts_input 要转换为时间戳的对象,可以是 datetime-like,str,int,float 类型 freq 时间戳将具有的偏移量,可以是 str,日期偏移量类型,取值参见【02x02】freq 频率部分取值 tz 时间戳将具有的时区 unit 如果 ts_input 是整数或浮点数,该参数用于设置其单位(D、s、ms、us、ns) 简单示例: 123>>> import pandas as pd>>> pd.Timestamp('2017-01-01T12')Timestamp('2017-01-01 12:00:00') 设置 unit='s',即待转换对象单位为秒: 123>>> import pandas as pd>>> pd.Timestamp(1513393355.5, unit='s')Timestamp('2017-12-16 03:02:35.500000') 使用 tz 参数设置时区: 123>>> import pandas as pd>>> pd.Timestamp(1513393355, unit='s', tz='US/Pacific')Timestamp('2017-12-15 19:02:35-0800', tz='US/Pacific') 单独设置年月日: 123>>> import pandas as pd>>> pd.Timestamp(year=2020, month=6, day=24, hour=12)Timestamp('2020-06-24 12:00:00') 【02x02】freq 频率部分取值完整取值参见官方文档:https://pandas.pydata.org/docs/user_guide/timeseries.html#timeseries-offset-aliases 参数 类型 描述 D Day 每日历日 B BusinessDay 每工作日 H Hour 每小时 T 或 min Minute 每分 S Second 每秒 L 或 ms Milli 每毫秒(即每千分之一秒) U Micro 每微秒(即每百万分之一秒) M MonthEnd 每月最后一个日历日 BM BusinessMonthEnd 每月最后一个工作日 MS MonthBegin 每月第一个日历日 BMS BusinessMonthBegin 每月第一个工作日 W-MON、W-TUE… Week 从指定的星期几(MON、TUE、 WED、THU、FR、SAT、SUN)开始算起,每周 WoM-1MON、WOM-2MON… WeekOfMonth 产生每月第一、第二、第三或第四周的星期几。例如,WoM-3FRI 表示每月第3个星期五 Q-JAN、Q-FEB… QuarterEnd 对于以指定月份(JAN、FEB、MAR、APR、MAY、JUN、JUL、AUG、SEP、OCT、NOV、DEC)结束的年度,每季度最后一月的最后个日历日 BQ-JAN、BQ-FEB… BusinessQuarterEnd 对于以指定月份结束的年度,每季度最后一月的最后一个工作日 QS-JAN、QS-FEB… QuarterBegin 对于以指定月份结束的年度,每季度最后一月的第一个日历日 BQS-JAN、 BQS-FEB… BusinessQuarterBegin 对于以指定月份结束的年度,每季度最后一月的第一个工作日 A-JAN、A-FEB… YearEnd 每年指定月份(JAN、FEB、MAR、APR、MAY、JUN、JUL、AUG、SEP、 OCT、NOV、DEC)的最后一个日历日 BA-JAN、BA-FEB… BusinessYearEnd 每年指定月份的最后一个工作日 AS-JAN、AS-FEB… YearBegin 每年指定月份的第一个历日日 BAS-JAN、BAS-FEB… BusinessYearBegin 每年指定月份的第一个工作日 【02x03】to_datetime在 Python 中,datetime 库提供了日期和时间处理方法,利用 str 或 strftime 方法可以将 datetime 对象转化成字符串,具体用法可参见【Python 标准库学习】日期和时间处理库 — datetime。 12345678910>>> from datetime import datetime>>> stamp = datetime(2020, 6, 24)>>> stampdatetime.datetime(2020, 6, 24, 0, 0)>>>>>> str(stamp)'2020-06-24 00:00:00'>>> >>> stamp.strftime('%Y-%m-%d')'2020-06-24' 在 pandas 中 to_datetime 方法可以将字符串解析成多种不同的 Timestamp(时间戳) 对象: 1234567>>> import pandas as pd>>> datestrs = '2011-07-06 12:00:00'>>> type(datestrs)<class 'str'>>>> >>> pd.to_datetime(datestrs)Timestamp('2011-07-06 12:00:00') 基本语法: 1234pandas.to_datetime(arg, errors='raise', dayfirst=False, yearfirst=False, utc=None, format=None, exact=True, unit=None, infer_datetime_format=False, origin='unix', cache=True) 官方文档:https://pandas.pydata.org/docs/reference/api/pandas.to_datetime.html 常用参数: 参数 描述 arg 要转换为日期时间的对象,可以接受 int, float, str, datetime, list, tuple, 1-d array, Series DataFrame/dict-like 类型 errors 如果字符串不满足时间戳的形式,是否会发生异常ignore:不引发异常,返回原始输入;raise:无效解析将引发异常(默认);coerce:无效解析将被设置为NaT dayfirst bool 类型,默认 False,如果 arg 是 str 或列表,是否首先解析为日期例如 dayfirst 为 True,10/11/12 被解析为 2012-11-10,为 False 则解析为 2012-10-11 yearfirst bool 类型,默认 False,如果 arg 是 str 或列表,是否首先解析为年份例如 dayfirst 为 True,10/11/12 被解析为 2010-11-12,为 False 则解析为 2012-10-11如果 dayfirst 和 yearfirst 都为 True,则优先 yearfirst utc bool 类型,是否转换为协调世界时,默认 None format 格式化时间,如 21/2/20 16:10 使用 %d/%m/%y %H:%M 会被解析为 2020-02-21 16:10:00符号含义常见文章:【Python 标准库学习】日期和时间处理库 — datetime 或者官方文档 exact 如果为 True,则需要精确的格式匹配。如果为 False,则允许格式与目标字符串中的任何位置匹配 unit 如果 arg 是整数或浮点数,该参数用于设置其单位(D、s、ms、us、ns) 简单应用: 1234567891011>>> import pandas as pd>>> obj = pd.DataFrame({'year': [2015, 2016], 'month': [2, 3], 'day': [4, 5]})>>> obj year month day0 2015 2 41 2016 3 5>>> >>> pd.to_datetime(obj)0 2015-02-041 2016-03-05dtype: datetime64[ns] 设置 format 和 errors 参数: 1234567891011>>> import pandas as pd>>> pd.to_datetime('13000101', format='%Y%m%d', errors='ignore')datetime.datetime(1300, 1, 1, 0, 0)>>> >>> pd.to_datetime('13000101', format='%Y%m%d', errors='coerce')NaT>>> >>> pd.to_datetime('13000101', format='%Y%m%d', errors='raise')Traceback (most recent call last):...pandas._libs.tslibs.np_datetime.OutOfBoundsDatetime: Out of bounds nanosecond timestamp: 1300-01-01 00:00:00 设置 unit 参数: 123456>>> import pandas as pd>>> pd.to_datetime(1490195805, unit='s')Timestamp('2017-03-22 15:16:45')>>> >>> pd.to_datetime(1490195805433502912, unit='ns')Timestamp('2017-03-22 15:16:45.433502912') 【02x04】date_rangepandas.date_range 方法可用于根据指定的频率生成指定长度的 DatetimeIndex。 基本语法: 123pandas.date_range(start=None, end=None, periods=None, freq=None, tz=None, normalize=False, name=None, closed=None, **kwargs) → pandas.core.indexes.datetimes.DatetimeIndex 官方文档:https://pandas.pydata.org/docs/reference/api/pandas.date_range.html 参数 描述 start 开始日期 end 结束日期 periods int 类型,要生成的时段数(天) freq 频率字符串,即按照某种特定的频率来生成日期,取值参见【02x02】freq 频率部分取值 tz 设置时区,例如 “Asia/Hong_Kong” normalize bool 类型,默认 False,是否在生成日期之前对其进行规范化(仅保留年月日) name 结果 DatetimeIndex 的名称 closed None:默认值,同时保留开始日期和结束日期'left':保留开始日期,不保留结束日期'right':保留结束日期,不保留开始日期 简单示例: 12345>>> import pandas as pd>>> pd.date_range(start='1/1/2018', end='1/08/2018')DatetimeIndex(['2018-01-01', '2018-01-02', '2018-01-03', '2018-01-04', '2018-01-05', '2018-01-06', '2018-01-07', '2018-01-08'], dtype='datetime64[ns]', freq='D') 指定 periods 参数: 1234567891011121314151617181920212223>>> import pandas as pd>>> pd.date_range(start='2012-04-01', periods=20)DatetimeIndex(['2012-04-01', '2012-04-02', '2012-04-03', '2012-04-04', '2012-04-05', '2012-04-06', '2012-04-07', '2012-04-08', '2012-04-09', '2012-04-10', '2012-04-11', '2012-04-12', '2012-04-13', '2012-04-14', '2012-04-15', '2012-04-16', '2012-04-17', '2012-04-18', '2012-04-19', '2012-04-20'], dtype='datetime64[ns]', freq='D')>>> >>> pd.date_range(end='2012-06-01', periods=20)DatetimeIndex(['2012-05-13', '2012-05-14', '2012-05-15', '2012-05-16', '2012-05-17', '2012-05-18', '2012-05-19', '2012-05-20', '2012-05-21', '2012-05-22', '2012-05-23', '2012-05-24', '2012-05-25', '2012-05-26', '2012-05-27', '2012-05-28', '2012-05-29', '2012-05-30', '2012-05-31', '2012-06-01'], dtype='datetime64[ns]', freq='D')>>>>>> pd.date_range(start='2018-04-24', end='2018-04-27', periods=3)DatetimeIndex(['2018-04-24 00:00:00', '2018-04-25 12:00:00', '2018-04-27 00:00:00'], dtype='datetime64[ns]', freq=None)>>>>>> pd.date_range(start='2018-04-24', end='2018-04-28', periods=3)DatetimeIndex(['2018-04-24', '2018-04-26', '2018-04-28'], dtype='datetime64[ns]', freq=None) 指定 freq='M' 会按照每月最后一个日历日的频率生成日期,指定 freq='3M' 会每隔3个月按照每月最后一个日历日的频率生成日期: 1234567891011>>> import pandas as pd>>> pd.date_range(start='1/1/2018', periods=5, freq='M')DatetimeIndex(['2018-01-31', '2018-02-28', '2018-03-31', '2018-04-30', '2018-05-31'], dtype='datetime64[ns]', freq='M')>>> >>> pd.date_range(start='1/1/2018', periods=5, freq='3M')DatetimeIndex(['2018-01-31', '2018-04-30', '2018-07-31', '2018-10-31', '2019-01-31'], dtype='datetime64[ns]', freq='3M')>>> 使用 tz 参数设置时区: 123456789101112>>> import pandas as pd>>> pd.date_range(start='1/1/2018', periods=5, tz='Asia/Tokyo')DatetimeIndex(['2018-01-01 00:00:00+09:00', '2018-01-02 00:00:00+09:00', '2018-01-03 00:00:00+09:00', '2018-01-04 00:00:00+09:00', '2018-01-05 00:00:00+09:00'], dtype='datetime64[ns, Asia/Tokyo]', freq='D')>>> >>> pd.date_range(start='6/24/2020', periods=5, tz='Asia/Hong_Kong')DatetimeIndex(['2020-06-24 00:00:00+08:00', '2020-06-25 00:00:00+08:00', '2020-06-26 00:00:00+08:00', '2020-06-27 00:00:00+08:00', '2020-06-28 00:00:00+08:00'], dtype='datetime64[ns, Asia/Hong_Kong]', freq='D') 设置 normalize 参数,在生成时间戳之前对其进行格式化操作: 12345>>> import pandas as pd>>> pd.date_range('2020-06-24 12:56:31', periods=5, normalize=True)DatetimeIndex(['2020-06-24', '2020-06-25', '2020-06-26', '2020-06-27', '2020-06-28'], dtype='datetime64[ns]', freq='D') 设置 closed 参数: 1234567891011>>> import pandas as pd>>> pd.date_range(start='2020-06-20', end='2020-06-24', closed=None)DatetimeIndex(['2020-06-20', '2020-06-21', '2020-06-22', '2020-06-23', '2020-06-24'], dtype='datetime64[ns]', freq='D')>>> >>> pd.date_range(start='2020-06-20', end='2020-06-24', closed='left')DatetimeIndex(['2020-06-20', '2020-06-21', '2020-06-22', '2020-06-23'], dtype='datetime64[ns]', freq='D')>>> >>> pd.date_range(start='2020-06-20', end='2020-06-24', closed='right')DatetimeIndex(['2020-06-21', '2020-06-22', '2020-06-23', '2020-06-24'], dtype='datetime64[ns]', freq='D') 【02x05】索引与切片Pandas 最基本的时间序列类型就是以时间戳(通常以 Python 字符串或 datatime 对象表示)为索引的Series,这些 datetime 对象实际上是被放在 DatetimeIndex 中的,可以使用类似 pandas.Series 对象的切片方法对其进行索引: 1234567891011121314151617181920212223242526>>> import pandas as pd>>> import numpy as np>>> dates = [datetime(2011, 1, 2), datetime(2011, 1, 5), datetime(2011, 1, 7), datetime(2011, 1, 8), datetime(2011, 1, 10), datetime(2011, 1, 12)]>>> obj = pd.Series(np.random.randn(6), index=dates)>>> >>> obj2011-01-02 -0.4071102011-01-05 -0.1866612011-01-07 -0.7310802011-01-08 0.8609702011-01-10 1.9299732011-01-12 -0.168599dtype: float64>>> >>> obj.indexDatetimeIndex(['2011-01-02', '2011-01-05', '2011-01-07', '2011-01-08', '2011-01-10', '2011-01-12'], dtype='datetime64[ns]', freq=None)>>>>>> obj.index[0]Timestamp('2011-01-02 00:00:00')>>> >>> obj.index[0:3]DatetimeIndex(['2011-01-02', '2011-01-05', '2011-01-07'], dtype='datetime64[ns]', freq=None) 另外还可以传入一个可以被解释为日期的字符串,或者只需传入“年”或“年月”即可轻松选取数据的切片: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172>>> import pandas as pd>>> import numpy as np>>> obj = pd.Series(np.random.randn(1000), index=pd.date_range('1/1/2000', periods=1000))>>> obj2000-01-01 -1.1422842000-01-02 1.1987852000-01-03 2.4669092000-01-04 -0.0867282000-01-05 -0.978437 ... 2002-09-22 -0.2522402002-09-23 0.1485612002-09-24 -1.3304092002-09-25 -0.6734712002-09-26 -0.253271Freq: D, Length: 1000, dtype: float64>>> >>> obj['26/9/2002']-0.25327100684233356>>> >>> obj['2002']2002-01-01 1.0587152002-01-02 0.9008592002-01-03 1.9935082002-01-04 -0.1032112002-01-05 -0.950090 ... 2002-09-22 -0.2522402002-09-23 0.1485612002-09-24 -1.3304092002-09-25 -0.6734712002-09-26 -0.253271Freq: D, Length: 269, dtype: float64>>> >>> obj['2002-09']2002-09-01 -0.9955282002-09-02 0.5015282002-09-03 -0.4867532002-09-04 -1.0839062002-09-05 1.4589752002-09-06 -1.3316852002-09-07 0.1953382002-09-08 -0.4296132002-09-09 1.1258232002-09-10 1.6070512002-09-11 0.5303872002-09-12 -0.0159382002-09-13 1.7810432002-09-14 -0.2771232002-09-15 0.3445692002-09-16 -1.0108102002-09-17 0.4630012002-09-18 1.8836362002-09-19 0.2745202002-09-20 0.6241842002-09-21 -1.2030572002-09-22 -0.2522402002-09-23 0.1485612002-09-24 -1.3304092002-09-25 -0.6734712002-09-26 -0.253271Freq: D, dtype: float64>>> >>> obj['20/9/2002':'26/9/2002']2002-09-20 0.6241842002-09-21 -1.2030572002-09-22 -0.2522402002-09-23 0.1485612002-09-24 -1.3304092002-09-25 -0.6734712002-09-26 -0.253271Freq: D, dtype: float64 【02x06】移动数据与数据偏移移动(shifting)指的是沿着时间轴将数据前移或后移。Series 和 DataFrame 都有一个 shift 方法用于执行单纯的前移或后移操作,保持索引不变: 123456789101112131415161718192021222324>>> import pandas as pd>>> import numpy as np>>> obj = pd.Series(np.random.randn(4), index=pd.date_range('1/1/2000', periods=4, freq='M'))>>> obj2000-01-31 -0.1002172000-02-29 1.1778342000-03-31 -0.6443532000-04-30 -1.954679Freq: M, dtype: float64>>> >>> obj.shift(2)2000-01-31 NaN2000-02-29 NaN2000-03-31 -0.1002172000-04-30 1.177834Freq: M, dtype: float64>>> >>> obj.shift(-2)2000-01-31 -0.6443532000-02-29 -1.9546792000-03-31 NaN2000-04-30 NaNFreq: M, dtype: float64 因为简单的移位操作不会修改索引,所以部分数据会被丢弃并引入 NaN(缺失值)。因此,如果频率已知,则可以将其传给 shift 以便实现对时间戳进行位移而不是对数据进行简单位移: 1234567891011121314151617>>> import pandas as pd>>> import numpy as np>>> obj = pd.Series(np.random.randn(4), index=pd.date_range('1/1/2000', periods=4, freq='M'))>>> obj2000-01-31 -0.1002172000-02-29 1.1778342000-03-31 -0.6443532000-04-30 -1.954679Freq: M, dtype: float64>>> >>> obj.shift(2, freq='M')2000-03-31 -0.1002172000-04-30 1.1778342000-05-31 -0.6443532000-06-30 -1.954679Freq: M, dtype: float64 Pandas 中的频率是由一个基础频率(base frequency)和一个乘数组成的。基础频率通常以一个字符串别名表示,比如 "M" 表示每月,"H" 表示每小时。对于每个基础频率,都有一个被称为日期偏移量(date offset)的对象与之对应。例如,按小时计算的频率可以用 Hour 类表示: 12345678>>> from pandas.tseries.offsets import Hour, Minute>>> hour = Hour()>>> hour<Hour>>>> >>> four_hours = Hour(4)>>> four_hours<4 * Hours> 一般来说,无需明确创建这样的对象,只需使用诸如 "H" 或 "4H" 这样的字符串别名即可。在基础频率前面放上一个整数即可创建倍数: 123456789101112>>> import pandas as pd>>> pd.date_range('2000-01-01', '2000-01-03 23:59', freq='4h')DatetimeIndex(['2000-01-01 00:00:00', '2000-01-01 04:00:00', '2000-01-01 08:00:00', '2000-01-01 12:00:00', '2000-01-01 16:00:00', '2000-01-01 20:00:00', '2000-01-02 00:00:00', '2000-01-02 04:00:00', '2000-01-02 08:00:00', '2000-01-02 12:00:00', '2000-01-02 16:00:00', '2000-01-02 20:00:00', '2000-01-03 00:00:00', '2000-01-03 04:00:00', '2000-01-03 08:00:00', '2000-01-03 12:00:00', '2000-01-03 16:00:00', '2000-01-03 20:00:00'], dtype='datetime64[ns]', freq='4H') 大部分偏移量对象都可通过加法进行连接: 123>>> from pandas.tseries.offsets import Hour, Minute>>> Hour(2) + Minute(30)<150 * Minutes> 对于 freq 参数也可以传入频率字符串(如 "2h30min"),这种字符串可以被高效地解析为等效的表达式: 12345678>>> import pandas as pd>>> pd.date_range('2000-01-01', periods=10, freq='1h30min')DatetimeIndex(['2000-01-01 00:00:00', '2000-01-01 01:30:00', '2000-01-01 03:00:00', '2000-01-01 04:30:00', '2000-01-01 06:00:00', '2000-01-01 07:30:00', '2000-01-01 09:00:00', '2000-01-01 10:30:00', '2000-01-01 12:00:00', '2000-01-01 13:30:00'], dtype='datetime64[ns]', freq='90T') 这种偏移量还可以用在 datetime 或 Timestamp 对象上: 1234>>> from pandas.tseries.offsets import Day, MonthEnd>>> now = datetime(2011, 11, 17)>>> now + 3 * Day()Timestamp('2011-11-20 00:00:00') 如果加的是锚点偏移量,比如 MonthEnd,第一次增量会将原日期向前滚动到符合频率规则的下一个日期: 123456>>> from pandas.tseries.offsets import Day, MonthEnd>>> now = datetime(2011, 11, 17)>>> now + MonthEnd()Timestamp('2011-11-30 00:00:00')>>> now + MonthEnd(2)Timestamp('2011-12-31 00:00:00') 通过锚点偏移量的 rollforward 和 rollback 方法,可明确地将日期向前或向后滚动: 1234567>>> from pandas.tseries.offsets import Day, MonthEnd>>> now = datetime(2011, 11, 17)>>> offset = MonthEnd()>>> offset.rollforward(now)Timestamp('2011-11-30 00:00:00')>>> offset.rollback(now)Timestamp('2011-10-31 00:00:00') 与 groupby 方法结合使用: 12345678910111213141516171819202122232425262728293031323334>>> import pandas as pd>>> import numpy as np>>> from pandas.tseries.offsets import Day, MonthEnd>>> obj = pd.Series(np.random.randn(20), index=pd.date_range('1/15/2000', periods=20, freq='4d'))>>> obj2000-01-15 -0.5917292000-01-19 -0.7758442000-01-23 -0.7456032000-01-27 -0.0764392000-01-31 1.7964172000-02-04 -0.5003492000-02-08 0.5158512000-02-12 -0.3441712000-02-16 0.4196572000-02-20 0.3072882000-02-24 0.1151132000-02-28 -0.3625852000-03-03 1.0748922000-03-07 1.1113662000-03-11 0.9499102000-03-15 -1.5357272000-03-19 0.5459442000-03-23 -0.8101392000-03-27 -1.2606272000-03-31 -0.128403Freq: 4D, dtype: float64>>>>>> offset = MonthEnd()>>> obj.groupby(offset.rollforward).mean()2000-01-31 -0.0786402000-02-29 0.0215432000-03-31 -0.006598dtype: float64 【02x07】时区处理在 Python 中,时区信息来自第三方库 pytz,使用 pytz.common_timezones 方法可以查看所有的时区名称,使用 pytz.timezone 方法从 pytz 中获取时区对象: 1234567>>> import pytz>>> pytz.common_timezones['Africa/Abidjan', 'Africa/Accra', 'Africa/Addis_Ababa', ..., 'UTC']>>>>>> tz = pytz.timezone('Asia/Shanghai')>>> tz<DstTzInfo 'Asia/Shanghai' LMT+8:06:00 STD> # 表示与 UTC 时间相差8小时6分 在 date_range 方法中,tz 参数用于指定时区,默认为 None,可以使用 tz_localize 方法将其进行本地化时区转换,如下示例中,将无时区转本地化 UTC 时区: 12345678910111213141516171819202122232425262728293031>>> import pandas as pd>>> import numpy as np>>> rng = pd.date_range('3/9/2012 9:30', periods=6, freq='D')>>> ts = pd.Series(np.random.randn(len(rng)), index=rng)>>> ts2012-03-09 09:30:00 -1.5279132012-03-10 09:30:00 -1.1161012012-03-11 09:30:00 0.3593582012-03-12 09:30:00 -0.4759202012-03-13 09:30:00 -0.3365702012-03-14 09:30:00 -1.075952Freq: D, dtype: float64>>> >>> print(ts.index.tz)None>>> >>> ts_utc = ts.tz_localize('UTC')>>> ts_utc2012-03-09 09:30:00+00:00 -1.5279132012-03-10 09:30:00+00:00 -1.1161012012-03-11 09:30:00+00:00 0.3593582012-03-12 09:30:00+00:00 -0.4759202012-03-13 09:30:00+00:00 -0.3365702012-03-14 09:30:00+00:00 -1.075952Freq: D, dtype: float64>>>>>> ts_utc.indexDatetimeIndex(['2012-03-09 09:30:00+00:00', '2012-03-10 09:30:00+00:00', '2012-03-11 09:30:00+00:00', '2012-03-12 09:30:00+00:00', '2012-03-13 09:30:00+00:00', '2012-03-14 09:30:00+00:00'], dtype='datetime64[ns, UTC]', freq='D') 时间序列被本地化到某个特定时区后,就可以用 tz_convert 方法将其转换到别的时区了: 123456789101112131415161718192021>>> import pandas as pd>>> import numpy as np>>> rng = pd.date_range('3/9/2012 9:30', periods=6, freq='D')>>> ts = pd.Series(np.random.randn(len(rng)), index=rng)>>> ts2012-03-09 09:30:00 0.4803032012-03-10 09:30:00 -1.4610392012-03-11 09:30:00 -1.5127492012-03-12 09:30:00 -2.1854212012-03-13 09:30:00 1.6578452012-03-14 09:30:00 0.175633Freq: D, dtype: float64>>>>>> ts.tz_localize('UTC').tz_convert('Asia/Shanghai')2012-03-09 17:30:00+08:00 0.4803032012-03-10 17:30:00+08:00 -1.4610392012-03-11 17:30:00+08:00 -1.5127492012-03-12 17:30:00+08:00 -2.1854212012-03-13 17:30:00+08:00 1.6578452012-03-14 17:30:00+08:00 0.175633Freq: D, dtype: float64 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106947061未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【03x00】period 固定时期【03x01】pandas.Period固定时期(period)表示的是时间区间,比如数日、数月、数季、数年等。Period 类所表示的就是这种数据类型,其构造函数需要用到一个字符串或整数。 基本语法: 123class pandas.Period(value=None, freq=None, ordinal=None, year=None, month=None, quarter=None, day=None, hour=None, minute=None, second=None) 官方文档:https://pandas.pydata.org/docs/reference/api/pandas.Period.html 常用参数: 参数 描述 value 时间段 freq 时间戳将具有的偏移量,可以是 str,日期偏移量类型,取值参见【02x02】freq 频率部分取值 以下示例中,Period 对象表示的是从2020年1月1日到2020年12月31日之间的整段时间 123>>> import pandas as pd>>> pd.Period(2020, freq='A-DEC')Period('2020', 'A-DEC') 利用加减法对其按照频率进行位移: 12345678910>>> import pandas as pd>>> obj = pd.Period(2020, freq='A-DEC')>>> objPeriod('2020', 'A-DEC')>>> >>> obj + 5Period('2025', 'A-DEC')>>> >>> obj - 5Period('2015', 'A-DEC') PeriodIndex 类保存了一组 Period,它可以在任何 pandas 数据结构中被用作轴索引: 12345678910111213141516>>> import pandas as pd>>> import numpy as np>>> rng = [pd.Period('2000-01'), pd.Period('2000-02'), pd.Period('2000-03'), pd.Period('2000-04'), pd.Period('2000-05'), pd.Period('2000-06')]>>> obj = pd.Series(np.random.randn(6), index=rng)>>> obj2000-01 0.2290922000-02 1.5154982000-03 -0.3344012000-04 -0.4926812000-05 -2.0128182000-06 0.338804Freq: M, dtype: float64>>> >>> obj.indexPeriodIndex(['2000-01', '2000-02', '2000-03', '2000-04', '2000-05', '2000-06'], dtype='period[M]', freq='M') 123456>>> import pandas as pd>>> values = ['2001Q3', '2002Q2', '2003Q1']>>> index = pd.PeriodIndex(values, freq='Q-DEC')>>> indexPeriodIndex(['2001Q3', '2002Q2', '2003Q1'], dtype='period[Q-DEC]', freq='Q-DEC')>>> 【03x02】period_rangepandas.period_range 方法可根据指定的频率生成指定长度的 PeriodIndex。 基本语法: pandas.period_range(start=None, end=None, periods=None, freq=None, name=None) → pandas.core.indexes.period.PeriodIndex 官方文档:https://pandas.pydata.org/docs/reference/api/pandas.period_range.html 常用参数: 参数 描述 start 起始日期 end 结束日期 periods 要生成的时段数 freq 时间戳将具有的偏移量,可以是 str,日期偏移量类型,取值参见【02x02】freq 频率部分取值 name 结果 PeriodIndex 对象名称 简单应用: 12345678910>>> import pandas as pd>>> pd.period_range(start='2019-01-01', end='2020-01-01', freq='M')PeriodIndex(['2019-01', '2019-02', '2019-03', '2019-04', '2019-05', '2019-06', '2019-07', '2019-08', '2019-09', '2019-10', '2019-11', '2019-12', '2020-01'], dtype='period[M]', freq='M')>>>>>> pd.period_range(start=pd.Period('2017Q1', freq='Q'), end=pd.Period('2017Q2', freq='Q'), freq='M')PeriodIndex(['2017-03', '2017-04', '2017-05', '2017-06'], dtype='period[M]', freq='M') 【03x03】asfreq 时期频率转换Period 和 PeriodIndex 对象都可以通过 asfreq 方法被转换成别的频率。 基本语法:PeriodIndex.asfreq(self, *args, **kwargs) 常用参数: 参数 描述 freq 新的频率(偏移量),取值参见【02x02】freq 频率部分取值 how 按照开始或者结束对齐,'E' or 'END' or 'FINISH';'S' or 'START' or 'BEGIN' 应用示例: 12345678910>>> import pandas as pd>>> pidx = pd.period_range('2010-01-01', '2015-01-01', freq='A')>>> pidxPeriodIndex(['2010', '2011', '2012', '2013', '2014', '2015'], dtype='period[A-DEC]', freq='A-DEC')>>> >>> pidx.asfreq('M')PeriodIndex(['2010-12', '2011-12', '2012-12', '2013-12', '2014-12', '2015-12'], dtype='period[M]', freq='M')>>> >>> pidx.asfreq('M', how='S')PeriodIndex(['2010-01', '2011-01', '2012-01', '2013-01', '2014-01', '2015-01'], dtype='period[M]', freq='M') 【03x04】to_period 与 to_timestamp()to_period 方法可以将 Timestamp(时间戳) 转换为 Period(固定时期); to_timestamp 方法可以将 Period(固定时期)转换为 Timestamp(时间戳) 。 12345678910111213141516171819202122232425262728293031>>> import pandas as pd>>> rng = pd.date_range('2000-01-01', periods=3, freq='M')>>> ts = pd.Series(np.random.randn(3), index=rng)>>> ts2000-01-31 0.2207592000-02-29 -0.1082212000-03-31 0.819433Freq: M, dtype: float64>>> >>> pts = ts.to_period()>>> pts2000-01 0.2207592000-02 -0.1082212000-03 0.819433Freq: M, dtype: float64>>> >>> pts2 = pts.to_timestamp()>>> pts22000-01-01 0.2207592000-02-01 -0.1082212000-03-01 0.819433Freq: MS, dtype: float64>>> >>> ts.indexDatetimeIndex(['2000-01-31', '2000-02-29', '2000-03-31'], dtype='datetime64[ns]', freq='M')>>> >>> pts.indexPeriodIndex(['2000-01', '2000-02', '2000-03'], dtype='period[M]', freq='M')>>> >>> pts2.indexDatetimeIndex(['2000-01-01', '2000-02-01', '2000-03-01'], dtype='datetime64[ns]', freq='MS') 【04x00】timedelta 时间间隔【04x01】pandas.TimedeltaTimedelta 表示持续时间,即两个日期或时间之间的差。 Timedelta 相当于 Python 的 datetime.timedelta,在大多数情况下两者可以互换。 基本语法:class pandas.Timedelta(value=<object object>, unit=None, **kwargs) 官方文档:https://pandas.pydata.org/docs/reference/api/pandas.Timedelta.html 常用参数: 参数 描述 value 传入的值,可以是 Timedelta,timedelta,np.timedelta64,string 或 integer 对象 unit 用于设置 value 的单位,具体取值参见官方文档 表示两个 datetime 对象之间的时间差: 123>>> import pandas as pd>>> pd.to_datetime('2020-6-24') - pd.to_datetime('2016-1-1')Timedelta('1636 days 00:00:00') 通过字符串传递参数: 123>>> import pandas as pd>>> pd.Timedelta('3 days 3 hours 3 minutes 30 seconds')Timedelta('3 days 03:03:30') 通过整数传递参数: 123>>> import pandas as pd>>> pd.Timedelta(5,unit='h')Timedelta('0 days 05:00:00') 获取属性: 123456789>>> import pandas as pd>>> obj = pd.Timedelta('3 days 3 hours 3 minutes 30 seconds')>>> objTimedelta('3 days 03:03:30')>>> >>> obj.days3>>> obj.seconds11010 【04x02】to_timedeltato_timedelta 方法可以将传入的对象转换成 timedelta 对象。 基本语法:pandas.to_timedelta(arg, unit='ns', errors='raise') 官方文档:https://pandas.pydata.org/docs/reference/api/pandas.to_timedelta.html 常用参数: 参数 描述 arg 要转换为 timedelta 的对象,可以是 str,timedelta,list-like 或 Series 对象 unit 用于设置 arg 的单位,具体取值参见官方文档 errors 如果 arg 不满足时间戳的形式,是否会发生异常ignore:不引发异常,返回原始输入;raise:无效解析将引发异常(默认);coerce:无效解析将被设置为NaT 将单个字符串解析为 timedelta 对象: 123456>>> import pandas as pd>>> pd.to_timedelta('1 days 06:05:01.00003')Timedelta('1 days 06:05:01.000030')>>>>>> pd.to_timedelta('15.5us')Timedelta('0 days 00:00:00.000015') 将字符串列表或数组解析为 timedelta 对象: 123>>> import pandas as pd>>> pd.to_timedelta(['1 days 06:05:01.00003', '15.5us', 'nan'])TimedeltaIndex(['1 days 06:05:01.000030', '0 days 00:00:00.000015', NaT], dtype='timedelta64[ns]', freq=None) 指定 unit 参数: 123456>>> import pandas as pd>>> pd.to_timedelta(np.arange(5), unit='s')TimedeltaIndex(['00:00:00', '00:00:01', '00:00:02', '00:00:03', '00:00:04'], dtype='timedelta64[ns]', freq=None)>>> >>> pd.to_timedelta(np.arange(5), unit='d')TimedeltaIndex(['0 days', '1 days', '2 days', '3 days', '4 days'], dtype='timedelta64[ns]', freq=None) 【04x03】timedelta_rangetimedelta_range 方法可根据指定的频率生成指定长度的 TimedeltaIndex。 基本语法: 12pandas.timedelta_range(start=None, end=None, periods=None, freq=None, name=None, closed=None) → pandas.core.indexes.timedeltas.TimedeltaIndex 官方文档:https://pandas.pydata.org/docs/reference/api/pandas.timedelta_range.html 常用参数: 参数 描述 start 开始日期 end 结束日期 periods int 类型,要生成的时段数 freq 频率字符串,即按照某种特定的频率来生成日期,取值参见【02x02】freq 频率部分取值 name 结果 TimedeltaIndex 的名称 closed None:默认值,同时保留开始日期和结束日期'left':保留开始日期,不保留结束日期'right':保留结束日期,不保留开始日期 应用示例: 123>>> import pandas as pd>>> pd.timedelta_range(start='1 day', periods=4)TimedeltaIndex(['1 days', '2 days', '3 days', '4 days'], dtype='timedelta64[ns]', freq='D') closed 参数指定保留哪个端点。默认保留两个端点: 123>>> import pandas as pd>>> pd.timedelta_range(start='1 day', periods=4, closed='right')TimedeltaIndex(['2 days', '3 days', '4 days'], dtype='timedelta64[ns]', freq='D') freq 参数指定 TimedeltaIndex 的频率。只接受固定频率,非固定频率如 'M' 将会报错: 12345678910>>> import pandas as pd>>> pd.timedelta_range(start='1 day', end='2 days', freq='6H')TimedeltaIndex(['1 days 00:00:00', '1 days 06:00:00', '1 days 12:00:00', '1 days 18:00:00', '2 days 00:00:00'], dtype='timedelta64[ns]', freq='6H')>>> >>> pd.timedelta_range(start='1 day', end='2 days', freq='M')Traceback (most recent call last):...ValueError: <MonthEnd> is a non-fixed frequency 【05x00】重采样及频率转换重采样(resampling)指的是将时间序列从一个频率转换到另一个频率的处理过程。将高频率数据聚合到低频率称为降采样(downsampling),而将低频率数据转换到高频率则称为升采样(upsampling)。并不是所有的重采样都能被划分到这两个大类中。例如,将 W-WED(每周三)转换为 W-FRI 既不是降采样也不是升采样。 Pandas 中提供了 resample 方法来帮助我们实现重采样。Pandas 对象都带有一个 resample 方法,它是各种频率转换工作的主力函数。 基本语法: 1234567Series.resample(self, rule, axis=0, closed: Union[str, NoneType] = None, label: Union[str, NoneType] = None, convention: str = 'start', kind: Union[str, NoneType] = None, loffset=None, base: int = 0, on=None, level=None) 1234567DataFrame.resample(self, rule, axis=0, closed: Union[str, NoneType] = None, label: Union[str, NoneType] = None, convention: str = 'start', kind: Union[str, NoneType] = None, loffset=None, base: int = 0, on=None, level=None) 常用参数: 参数 描述 rule axis 重采样的轴,默认 0 closed 在重采样中,各时间段的哪一端是闭合(即包含)的,除 'M'、'A'、'Q'、'BM'、'BA'、'BQ' 和 'W' 默认值为 ‘right’ 外,其他默认值为 ‘left‘ label 在重采样中,如何设置聚合值的标签, right 或 left,默认为 None,例如,9:30 到 9:35 之间的这 5 分钟会被标记为 9:30 或 9:35 convention 仅用于 PeriodIndex(固定时期),对周期进行重采样,'start' or 's','end' or 'e' on 对于 DataFrame 对象,可用该参数指定重采样后的数据的 index(行索引) 为原数据中的某列 level 对于具有层级索引(MultiIndex)的 DataFrame 对象,可以使用该参数来指定需要在哪个级别上进行重新采样 将序列重采样到三分钟的频率,并将每个频率的值相加: 1234567891011121314151617181920>>> import pandas as pd>>> index = pd.date_range('1/1/2000', periods=9, freq='T')>>> series = pd.Series(range(9), index=index)>>> series2000-01-01 00:00:00 02000-01-01 00:01:00 12000-01-01 00:02:00 22000-01-01 00:03:00 32000-01-01 00:04:00 42000-01-01 00:05:00 52000-01-01 00:06:00 62000-01-01 00:07:00 72000-01-01 00:08:00 8Freq: T, dtype: int64>>> >>> series.resample('3T').sum()2000-01-01 00:00:00 32000-01-01 00:03:00 122000-01-01 00:06:00 21Freq: 3T, dtype: int64 设置 label='right',即每个索引 index 会使用靠右侧(较大值)的标签: 1234567891011121314151617181920>>> import pandas as pd>>> index = pd.date_range('1/1/2000', periods=9, freq='T')>>> series = pd.Series(range(9), index=index)>>> series2000-01-01 00:00:00 02000-01-01 00:01:00 12000-01-01 00:02:00 22000-01-01 00:03:00 32000-01-01 00:04:00 42000-01-01 00:05:00 52000-01-01 00:06:00 62000-01-01 00:07:00 72000-01-01 00:08:00 8Freq: T, dtype: int64>>> >>> series.resample('3T', label='right').sum()2000-01-01 00:03:00 32000-01-01 00:06:00 122000-01-01 00:09:00 21Freq: 3T, dtype: int64 设置 closed='right',即结果中会包含原数据中最右侧(较大)的值: 123456789101112131415161718192021>>> import pandas as pd>>> index = pd.date_range('1/1/2000', periods=9, freq='T')>>> series = pd.Series(range(9), index=index)>>> series2000-01-01 00:00:00 02000-01-01 00:01:00 12000-01-01 00:02:00 22000-01-01 00:03:00 32000-01-01 00:04:00 42000-01-01 00:05:00 52000-01-01 00:06:00 62000-01-01 00:07:00 72000-01-01 00:08:00 8Freq: T, dtype: int64>>> >>> series.resample('3T', label='right', closed='right').sum()2000-01-01 00:00:00 02000-01-01 00:03:00 62000-01-01 00:06:00 152000-01-01 00:09:00 15Freq: 3T, dtype: int64 以下示例将序列重采样到30秒的频率,asfreq()[0:5] 用于选择前5行数据: 12345678910111213141516171819202122>>> import pandas as pd>>> index = pd.date_range('1/1/2000', periods=9, freq='T')>>> series = pd.Series(range(9), index=index)>>> series2000-01-01 00:00:00 02000-01-01 00:01:00 12000-01-01 00:02:00 22000-01-01 00:03:00 32000-01-01 00:04:00 42000-01-01 00:05:00 52000-01-01 00:06:00 62000-01-01 00:07:00 72000-01-01 00:08:00 8Freq: T, dtype: int64>>> >>> series.resample('30S').asfreq()[0:5]2000-01-01 00:00:00 0.02000-01-01 00:00:30 NaN2000-01-01 00:01:00 1.02000-01-01 00:01:30 NaN2000-01-01 00:02:00 2.0Freq: 30S, dtype: float64 使用 pad 方法向后填充缺失值(NaN): 12345678910111213141516171819202122>>> import pandas as pd>>> index = pd.date_range('1/1/2000', periods=9, freq='T')>>> series = pd.Series(range(9), index=index)>>> series2000-01-01 00:00:00 02000-01-01 00:01:00 12000-01-01 00:02:00 22000-01-01 00:03:00 32000-01-01 00:04:00 42000-01-01 00:05:00 52000-01-01 00:06:00 62000-01-01 00:07:00 72000-01-01 00:08:00 8Freq: T, dtype: int64>>> >>> series.resample('30S').pad()[0:5]2000-01-01 00:00:00 02000-01-01 00:00:30 02000-01-01 00:01:00 12000-01-01 00:01:30 12000-01-01 00:02:00 2Freq: 30S, dtype: int64 使用 bfill 方法向前填充缺失值(NaN): 12345678910111213141516171819202122>>> import pandas as pd>>> index = pd.date_range('1/1/2000', periods=9, freq='T')>>> series = pd.Series(range(9), index=index)>>> series2000-01-01 00:00:00 02000-01-01 00:01:00 12000-01-01 00:02:00 22000-01-01 00:03:00 32000-01-01 00:04:00 42000-01-01 00:05:00 52000-01-01 00:06:00 62000-01-01 00:07:00 72000-01-01 00:08:00 8Freq: T, dtype: int64>>> >>> series.resample('30S').bfill()[0:5]2000-01-01 00:00:00 02000-01-01 00:00:30 12000-01-01 00:01:00 12000-01-01 00:01:30 22000-01-01 00:02:00 2Freq: 30S, dtype: int64 通过 apply 方法传递自定义函数: 1234567891011121314151617181920212223>>> import pandas as pd>>> index = pd.date_range('1/1/2000', periods=9, freq='T')>>> series = pd.Series(range(9), index=index)>>> series2000-01-01 00:00:00 02000-01-01 00:01:00 12000-01-01 00:02:00 22000-01-01 00:03:00 32000-01-01 00:04:00 42000-01-01 00:05:00 52000-01-01 00:06:00 62000-01-01 00:07:00 72000-01-01 00:08:00 8Freq: T, dtype: int64>>> >>> def custom_resampler(array_like): return np.sum(array_like) + 5>>> series.resample('3T').apply(custom_resampler)2000-01-01 00:00:00 82000-01-01 00:03:00 172000-01-01 00:06:00 26Freq: 3T, dtype: int64 convention 参数的应用: 12345678910111213141516171819202122232425>>> import pandas as pd>>> s = pd.Series([1, 2], index=pd.period_range('2012-01-01', freq='A', periods=2))>>> s2012 12013 2Freq: A-DEC, dtype: int64>>> >>> s.resample('Q', convention='start').asfreq()2012Q1 1.02012Q2 NaN2012Q3 NaN2012Q4 NaN2013Q1 2.02013Q2 NaN2013Q3 NaN2013Q4 NaNFreq: Q-DEC, dtype: float64>>> >>> s.resample('Q', convention='end').asfreq()2012Q4 1.02013Q1 NaN2013Q2 NaN2013Q3 NaN2013Q4 2.0Freq: Q-DEC, dtype: float64 123456789101112131415161718192021222324252627282930313233343536>>> import pandas as pd>>> q = pd.Series([1, 2, 3, 4], index=pd.period_range('2018-01-01', freq='Q', periods=4))>>> q2018Q1 12018Q2 22018Q3 32018Q4 4Freq: Q-DEC, dtype: int64>>> >>> q.resample('M', convention='end').asfreq()2018-03 1.02018-04 NaN2018-05 NaN2018-06 2.02018-07 NaN2018-08 NaN2018-09 3.02018-10 NaN2018-11 NaN2018-12 4.0Freq: M, dtype: float64>>> >>> q.resample('M', convention='start').asfreq()2018-01 1.02018-02 NaN2018-03 NaN2018-04 2.02018-05 NaN2018-06 NaN2018-07 3.02018-08 NaN2018-09 NaN2018-10 4.02018-11 NaN2018-12 NaNFreq: M, dtype: float64 对于 DataFrame 对象,可以使用关键字 on 来指定原数据中的某列为重采样后数据的行索引: 123456789101112131415161718192021>>> import pandas as pd>>> d = dict({'price': [10, 11, 9, 13, 14, 18, 17, 19], 'volume': [50, 60, 40, 100, 50, 100, 40, 50]})>>> df = pd.DataFrame(d)>>> df['week_starting'] = pd.date_range('01/01/2018', periods=8, freq='W')>>> df price volume week_starting0 10 50 2018-01-071 11 60 2018-01-142 9 40 2018-01-213 13 100 2018-01-284 14 50 2018-02-045 18 100 2018-02-116 17 40 2018-02-187 19 50 2018-02-25>>> >>> df.resample('M', on='week_starting').mean() price volumeweek_starting 2018-01-31 10.75 62.52018-02-28 17.00 60.0 对于具有层级索引(MultiIndex)的 DataFrame 对象,可以使用关键字 level 来指定需要在哪个级别上进行重新采样: 12345678910111213141516171819202122>>> import pandas as pd>>> days = pd.date_range('1/1/2000', periods=4, freq='D')>>> d2 = dict({'price': [10, 11, 9, 13, 14, 18, 17, 19], 'volume': [50, 60, 40, 100, 50, 100, 40, 50]})>>> df2 = pd.DataFrame(d2, index=pd.MultiIndex.from_product([days, ['morning', 'afternoon']]))>>> df2 price volume2000-01-01 morning 10 50 afternoon 11 602000-01-02 morning 9 40 afternoon 13 1002000-01-03 morning 14 50 afternoon 18 1002000-01-04 morning 17 40 afternoon 19 50>>> >>> df2.resample('D', level=0).sum() price volume2000-01-01 21 1102000-01-02 22 1402000-01-03 32 1502000-01-04 36 90 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106947061未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Pandas","slug":"Python-数据分析/Pandas","permalink":"https://www.itrhx.com/categories/Python-数据分析/Pandas/"}],"tags":[{"name":"Pandas","slug":"Pandas","permalink":"https://www.itrhx.com/tags/Pandas/"},{"name":"时间序列","slug":"时间序列","permalink":"https://www.itrhx.com/tags/时间序列/"}]},{"title":"Python 数据分析三剑客之 Pandas(八):数据重塑/重复数据处理/数据替换","slug":"A86-Pandas-08","date":"2020-06-22T13:01:54.429Z","updated":"2020-07-06T13:45:25.429Z","comments":true,"path":"2020/06/22/A86-Pandas-08/","link":"","permalink":"https://www.itrhx.com/2020/06/22/A86-Pandas-08/","excerpt":"","text":"Pandas 系列文章: Python 数据分析三剑客之 Pandas(一):认识 Pandas 及其 Series、DataFrame 对象 Python 数据分析三剑客之 Pandas(二):Index 索引对象以及各种索引操作 Python 数据分析三剑客之 Pandas(三):算术运算与缺失值的处理 Python 数据分析三剑客之 Pandas(四):函数应用、映射、排序和层级索引 Python 数据分析三剑客之 Pandas(五):统计计算与统计描述 Python 数据分析三剑客之 Pandas(六):GroupBy 数据分裂、应用与合并 Python 数据分析三剑客之 Pandas(七):合并数据集 Python 数据分析三剑客之 Pandas(八):数据重塑、重复数据处理与数据替换 Python 数据分析三剑客之 Pandas(九):时间序列 Python 数据分析三剑客之 Pandas(十):数据读写 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106900748未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【01x00】数据重塑有许多用于重新排列表格型数据的基础运算。这些函数也称作重塑(reshape)或轴向旋转(pivot)运算。重塑层次化索引主要有以下两个方法: stack:将数据的列转换成行; unstack:将数据的行转换成列。 【01x01】stackstack 方法用于将数据的列转换成为行; 基本语法:DataFrame.stack(self, level=-1, dropna=True) 官方文档:https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.stack.html 参数 描述 level 从列转换到行,指定不同层级的列索引或列标签、由列索引或列标签组成的数组,默认-1 dropna bool 类型,是否删除重塑后数据中所有值为 NaN 的行,默认 True 单层列(Single level columns): 12345678910111213>>> import pandas as pd>>> obj = pd.DataFrame([[0, 1], [2, 3]], index=['cat', 'dog'], columns=['weight', 'height'])>>> obj weight heightcat 0 1dog 2 3>>> >>> obj.stack()cat weight 0 height 1dog weight 2 height 3dtype: int64 多层列(Multi level columns): 123456789101112131415>>> import pandas as pd>>> multicol = pd.MultiIndex.from_tuples([('weight', 'kg'), ('weight', 'pounds')])>>> obj = pd.DataFrame([[1, 2], [2, 4]], index=['cat', 'dog'], columns=multicol)>>> obj weight kg poundscat 1 2dog 2 4>>> >>> obj.stack() weightcat kg 1 pounds 2dog kg 2 pounds 4 缺失值填充: 123456789101112131415>>> import pandas as pd>>> multicol = pd.MultiIndex.from_tuples([('weight', 'kg'), ('height', 'm')])>>> obj = pd.DataFrame([[1.0, 2.0], [3.0, 4.0]], index=['cat', 'dog'], columns=multicol)>>> obj weight height kg mcat 1.0 2.0dog 3.0 4.0>>> >>> obj.stack() height weightcat kg NaN 1.0 m 2.0 NaNdog kg NaN 3.0 m 4.0 NaN 通过 level 参数指定不同层级的轴进行重塑: 1234567891011121314151617181920212223242526272829>>> import pandas as pd>>> multicol = pd.MultiIndex.from_tuples([('weight', 'kg'), ('height', 'm')])>>> obj = pd.DataFrame([[1.0, 2.0], [3.0, 4.0]], index=['cat', 'dog'], columns=multicol)>>> obj weight height kg mcat 1.0 2.0dog 3.0 4.0>>> >>> obj.stack(level=0) kg mcat height NaN 2.0 weight 1.0 NaNdog height NaN 4.0 weight 3.0 NaN>>> >>> obj.stack(level=1) height weightcat kg NaN 1.0 m 2.0 NaNdog kg NaN 3.0 m 4.0 NaN>>>>>> obj.stack(level=[0, 1])cat height m 2.0 weight kg 1.0dog height m 4.0 weight kg 3.0dtype: float64 对于重塑后的数据,若有一行的值均为 NaN,则默认会被删除,可以设置 dropna=False 来保留缺失值: 123456789101112131415161718192021>>> import pandas as pd>>> multicol = pd.MultiIndex.from_tuples([('weight', 'kg'), ('height', 'm')])>>> obj = pd.DataFrame([[None, 1.0], [2.0, 3.0]], index=['cat', 'dog'], columns=multicol)>>> obj weight height kg mcat NaN 1.0dog 2.0 3.0>>> >>> obj.stack(dropna=False) height weightcat kg NaN NaN m 1.0 NaNdog kg NaN 2.0 m 3.0 NaN>>> >>> obj.stack(dropna=True) height weightcat m 1.0 NaNdog kg NaN 2.0 m 3.0 NaN 【01x02】unstackunstack:将数据的行转换成列。 基本语法: Series.unstack(self, level=-1, fill_value=None) DataFrame.unstack(self, level=-1, fill_value=None) 官方文档: https://pandas.pydata.org/docs/reference/api/pandas.Series.unstack.html https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.unstack.html 参数 描述 level 从行转换到列,指定不同层级的行索引,默认-1 fill_value 用于替换 NaN 的值 在 Series 对象中的应用: 123456789101112131415161718>>> import pandas as pd>>> obj = pd.Series([1, 2, 3, 4], index=pd.MultiIndex.from_product([['one', 'two'], ['a', 'b']]))>>> objone a 1 b 2two a 3 b 4dtype: int64>>> >>> obj.unstack() a bone 1 2two 3 4>>> >>> obj.unstack(level=0) one twoa 1 3b 2 4 和 stack 方法类似,如果值不存在将会引入缺失值(NaN): 123456789101112131415161718>>> import pandas as pd>>> obj1 = pd.Series([0, 1, 2, 3], index=['a', 'b', 'c', 'd'])>>> obj2 = pd.Series([4, 5, 6], index=['c', 'd', 'e'])>>> obj3 = pd.concat([obj1, obj2], keys=['one', 'two'])>>> obj3one a 0 b 1 c 2 d 3two c 4 d 5 e 6dtype: int64>>> >>> obj3.unstack() a b c d eone 0.0 1.0 2.0 3.0 NaNtwo NaN NaN 4.0 5.0 6.0 在 DataFrame 对象中的应用: 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame(np.arange(6).reshape((2, 3)), index=pd.Index(['Ohio','Colorado'], name='state'), columns=pd.Index(['one', 'two', 'three'], name='number'))>>> objnumber one two threestate Ohio 0 1 2Colorado 3 4 5>>> >>> obj2 = obj.stack()>>> obj2state numberOhio one 0 two 1 three 2Colorado one 3 two 4 three 5dtype: int32>>> >>> obj3 = pd.DataFrame({'left': obj2, 'right': obj2 + 5}, columns=pd.Index(['left', 'right'], name='side'))>>> obj3side left rightstate number Ohio one 0 5 two 1 6 three 2 7Colorado one 3 8 two 4 9 three 5 10>>> >>> obj3.unstack('state')side left right state Ohio Colorado Ohio Coloradonumber one 0 3 5 8two 1 4 6 9three 2 5 7 10>>> >>> obj3.unstack('state').stack('side')state Colorado Ohionumber side one left 3 0 right 8 5two left 4 1 right 9 6three left 5 2 right 10 7 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106900748未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【02x00】重复数据处理 duplicated:判断是否为重复值; drop_duplicates:删除重复值。 【02x01】duplicatedduplicated 方法可以判断值是否为重复数据。 基本语法: Series.duplicated(self, keep='first') DataFrame.duplicated(self, subset: Union[Hashable, Sequence[Hashable], NoneType] = None, keep: Union[str, bool] = 'first') → ’Series’ 官方文档: https://pandas.pydata.org/docs/reference/api/pandas.Series.duplicated.html https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.duplicated.html 参数 描述 keep 标记重复项的方法,默认 'first''first':将非重复项和第一个重复项标记为 False,其他重复项标记为 True'last':将非重复项和最后一个重复项标记为 False,其他重复项标记为 TrueFalse:将所有重复项标记为 True,非重复项标记为 False subset 列标签或标签序列,在 DataFrame 对象中才有此参数,用于指定某列,仅标记该列的重复项,默认情况下将考虑所有列 默认情况下,对于每组重复的值,第一个出现的重复值标记为 False,其他重复项标记为 True,非重复项标记为 False,相当于 keep='first': 12345678910111213141516171819202122232425>>> import pandas as pd>>> obj = pd.Series(['lama', 'cow', 'lama', 'beetle', 'lama'])>>> obj0 lama1 cow2 lama3 beetle4 lamadtype: object>>> >>> obj.duplicated()0 False1 False2 True3 False4 Truedtype: bool>>>>>> obj.duplicated(keep='first')0 False1 False2 True3 False4 Truedtype: bool 设置 keep='last',将每组非重复项和最后一次出现的重复项标记为 False,其他重复项标记为 True,设置 keep=False,则所有重复项均为 True,其他值为 False: 12345678910111213141516171819202122232425>>> import pandas as pd>>> obj = pd.Series(['lama', 'cow', 'lama', 'beetle', 'lama'])>>> obj0 lama1 cow2 lama3 beetle4 lamadtype: object>>> >>> obj.duplicated(keep='last')0 True1 False2 True3 False4 Falsedtype: bool>>> >>> obj.duplicated(keep=False)0 True1 False2 True3 False4 Truedtype: bool 在 DataFrame 对象中,subset 参数用于指定某列,仅标记该列的重复项,默认情况下将考虑所有列: 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame({'data1' : ['a'] * 4 + ['b'] * 4, 'data2' : np.random.randint(0, 4, 8)})>>> obj data1 data20 a 01 a 02 a 03 a 34 b 35 b 36 b 07 b 2>>> >>> obj.duplicated()0 False1 True2 True3 False4 False5 True6 False7 Falsedtype: bool>>> >>> obj.duplicated(subset='data1')0 False1 True2 True3 True4 False5 True6 True7 Truedtype: bool>>> >>> obj.duplicated(subset='data2', keep='last')0 True1 True2 True3 True4 True5 False6 False7 Falsedtype: bool 【02x02】drop_duplicatesdrop_duplicates 方法会返回一个删除了重复值的序列。 基本语法: 1Series.drop_duplicates(self, keep='first', inplace=False) 12345DataFrame.drop_duplicates(self, subset: Union[Hashable, Sequence[Hashable], NoneType] = None, keep: Union[str, bool] = 'first', inplace: bool = False, ignore_index: bool = False) → Union[ForwardRef(‘DataFrame’), NoneType] 官方文档: https://pandas.pydata.org/docs/reference/api/pandas.Series.drop_duplicates.html https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.drop_duplicates.html 参数 描述 keep 删除重复项的方法,默认 'first''first':保留非重复项和第一个重复项,其他重复项标记均删除'last':保留非重复项和最后一个重复项,其他重复项删除False:将所有重复项删除,非重复项保留 inplace 是否返回删除重复项后的值,默认 False,若设置为 True,则不返回值,直接改变原数据 subset 列标签或标签序列,在 DataFrame 对象中才有此参数,用于指定某列,仅标记该列的重复项,默认情况下将考虑所有列 ignore_index bool 类型,在 DataFrame 对象中才有此参数,是否忽略原对象的轴标记,默认 False,如果为 True,则新对象的索引将是 0, 1, 2, …, n-1 keep 参数的使用: 123456789101112131415161718192021222324252627282930>>> import pandas as pd>>> obj = pd.Series(['lama', 'cow', 'lama', 'beetle', 'lama', 'hippo'], name='animal')>>> obj0 lama1 cow2 lama3 beetle4 lama5 hippoName: animal, dtype: object>>> >>> obj.drop_duplicates()0 lama1 cow3 beetle5 hippoName: animal, dtype: object>>> >>> obj.drop_duplicates(keep='last')1 cow3 beetle4 lama5 hippoName: animal, dtype: object>>> >>> obj.drop_duplicates(keep=False)1 cow3 beetle5 hippoName: animal, dtype: object 如果设置 inplace=True,则不会返回任何值,但原对象的值已被改变: 12345678910111213141516171819202122232425262728>>> import pandas as pd>>> obj1 = pd.Series(['lama', 'cow', 'lama', 'beetle', 'lama', 'hippo'], name='animal')>>> obj10 lama1 cow2 lama3 beetle4 lama5 hippoName: animal, dtype: object>>> >>> obj2 = obj1.drop_duplicates()>>> obj2 # 有返回值0 lama1 cow3 beetle5 hippoName: animal, dtype: object>>> >>> obj3 = obj1.drop_duplicates(inplace=True)>>> obj3 # 无返回值>>>>>> obj1 # 原对象的值已改变0 lama1 cow3 beetle5 hippoName: animal, dtype: object 在 DataFrame 对象中的使用: 12345678910111213141516171819202122232425262728293031323334>>> import numpy as np>>> import pandas as pd>>> obj = pd.DataFrame({'data1' : ['a'] * 4 + ['b'] * 4, 'data2' : np.random.randint(0, 4, 8)})>>> obj data1 data20 a 21 a 12 a 13 a 24 b 15 b 26 b 07 b 0>>> >>> obj.drop_duplicates() data1 data20 a 21 a 14 b 15 b 26 b 0>>> >>> obj.drop_duplicates(subset='data2') data1 data20 a 21 a 16 b 0>>> >>> obj.drop_duplicates(subset='data2', ignore_index=True) data1 data20 a 21 a 12 b 0 【03x00】数据替换【03x01】replacereplace 方法可以根据值的内容进行替换。 基本语法: Series.replace(self, to_replace=None, value=None, inplace=False, limit=None, regex=False, method='pad') DataFrame.replace(self, to_replace=None, value=None, inplace=False, limit=None, regex=False, method='pad') 官方文档: https://pandas.pydata.org/docs/reference/api/pandas.Series.replace.html https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.replace.html 常用参数: 参数 描述 to_replace 找到要替换值的方法,可以是:字符串、正则表达式、列表、字典、整数、浮点数、Series 对象或者 None使用不同参数的区别参见官方文档 value 用于替换匹配项的值, 对于 DataFrame,可以使用字典的值来指定每列要使用的值,还允许使用此类对象的正则表达式,字符串和列表或字典 inplace bool 类型,是否直接改变原数据且不返回值,默认 False regex bool 类型或者与 to_replace 相同的类型,当 to_replace 参数为正则表达式时,regex 应为 True,或者直接使用该参数代替 to_replace to_replace 和 value 参数只传入一个值,单个值替换单个值: 1234567891011121314151617>>> import pandas as pd>>> obj = pd.Series([0, 1, 2, 3, 4])>>> obj0 01 12 23 34 4dtype: int64>>> >>> obj.replace(0, 5)0 51 12 23 34 4dtype: int64 to_replace 传入多个值,value 传入一个值,多个值替换一个值: 1234567891011121314151617>>> import pandas as pd>>> obj = pd.Series([0, 1, 2, 3, 4])>>> obj0 01 12 23 34 4dtype: int64>>> >>> obj.replace([0, 1, 2, 3], 4)0 41 42 43 44 4dtype: int64 to_replace 和 value 参数都传入多个值,多个值替换多个值: 1234567891011121314151617>>> import pandas as pd>>> obj = pd.Series([0, 1, 2, 3, 4])>>> obj0 01 12 23 34 4dtype: int64>>> >>> obj.replace([0, 1, 2, 3], [4, 3, 2, 1])0 41 32 23 14 4dtype: int64 to_replace 传入字典: 123456789101112131415161718192021222324252627282930313233343536373839404142>>> import pandas as pd>>> obj = pd.DataFrame({'A': [0, 1, 2, 3, 4], 'B': [5, 6, 7, 8, 9], 'C': ['a', 'b', 'c', 'd', 'e']})>>> obj A B C0 0 5 a1 1 6 b2 2 7 c3 3 8 d4 4 9 e>>> >>> obj.replace(0, 5) A B C0 5 5 a1 1 6 b2 2 7 c3 3 8 d4 4 9 e>>> >>> obj.replace({0: 10, 1: 100}) A B C0 10 5 a1 100 6 b2 2 7 c3 3 8 d4 4 9 e>>> >>> obj.replace({'A': 0, 'B': 5}, 100) A B C0 100 100 a1 1 6 b2 2 7 c3 3 8 d4 4 9 e>>> obj.replace({'A': {0: 100, 4: 400}}) A B C0 100 5 a1 1 6 b2 2 7 c3 3 8 d4 400 9 e to_replace 传入正则表达式: 1234567891011121314151617181920212223242526272829303132333435363738>>> import pandas as pd>>> obj = pd.DataFrame({'A': ['bat', 'foo', 'bait'], 'B': ['abc', 'bar', 'xyz']})>>> obj A B0 bat abc1 foo bar2 bait xyz>>> >>> obj.replace(to_replace=r'^ba.$', value='new', regex=True) A B0 new abc1 foo new2 bait xyz>>> >>> obj.replace({'A': r'^ba.$'}, {'A': 'new'}, regex=True) A B0 new abc1 foo bar2 bait xyz>>> >>> obj.replace(regex=r'^ba.$', value='new') A B0 new abc1 foo new2 bait xyz>>> >>> obj.replace(regex={r'^ba.$': 'new', 'foo': 'xyz'}) A B0 new abc1 xyz new2 bait xyz>>> >>> obj.replace(regex=[r'^ba.$', 'foo'], value='new') A B0 new abc1 new new2 bait xyz 【03x02】wherewhere 方法用于替换条件为 False 的值。 基本语法: Series.where(self, cond, other=nan, inplace=False, axis=None, level=None, errors='raise', try_cast=False) DataFrame.where(self, cond, other=nan, inplace=False, axis=None, level=None, errors='raise', try_cast=False) 官方文档: https://pandas.pydata.org/docs/reference/api/pandas.Series.where.html https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.where.html 常用参数: 参数 描述 cond 替换条件,如果 cond 为 True,则保留原始值。如果为 False,则替换为来自 other 的相应值 other 替换值,如果 cond 为 False,则替换为来自该参数的相应值 inplace bool 类型,是否直接改变原数据且不返回值,默认 False 在 Series 中的应用: 12345678910111213141516171819202122232425>>> import pandas as pd>>> obj = pd.Series(range(5))>>> obj0 01 12 23 34 4dtype: int64>>> >>> obj.where(obj > 0)0 NaN1 1.02 2.03 3.04 4.0dtype: float64>>> >>> obj.where(obj > 1, 10)0 101 102 23 34 4dtype: int64 在 DataFrame 中的应用: 1234567891011121314151617181920212223242526>>> import pandas as pd>>> obj = pd.DataFrame(np.arange(10).reshape(-1, 2), columns=['A', 'B'])>>> obj A B0 0 11 2 32 4 53 6 74 8 9>>> >>> m = obj % 3 == 0>>> obj.where(m, -obj) A B0 0 -11 -2 32 -4 -53 6 -74 -8 9>>> >>> obj.where(m, -obj) == np.where(m, obj, -obj) A B0 True True1 True True2 True True3 True True4 True True 【03x03】maskmask 方法与 where 方法相反,mask 用于替换条件为 False 的值。 基本语法: Series.mask(self, cond, other=nan, inplace=False, axis=None, level=None, errors='raise', try_cast=False) DataFrame.mask(self, cond, other=nan, inplace=False, axis=None, level=None, errors='raise', try_cast=False) 官方文档: https://pandas.pydata.org/docs/reference/api/pandas.Series.mask.html https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.mask.html 常用参数: 参数 描述 cond 替换条件,如果 cond 为 False,则保留原始值。如果为 True,则替换为来自 other 的相应值 other 替换值,如果 cond 为 False,则替换为来自该参数的相应值 inplace bool 类型,是否直接改变原数据且不返回值,默认 False 在 Series 中的应用: 12345678910111213141516171819202122232425>>> import pandas as pd>>> obj = pd.Series(range(5))>>> obj0 01 12 23 34 4dtype: int64>>> >>> obj.mask(obj > 0)0 0.01 NaN2 NaN3 NaN4 NaNdtype: float64>>> >>> obj.mask(obj > 1, 10)0 01 12 103 104 10dtype: int64 在 DataFrame 中的应用: 123456789101112131415161718192021222324252627>>> import pandas as pd>>> obj = pd.DataFrame(np.arange(10).reshape(-1, 2), columns=['A', 'B'])>>> obj A B0 0 11 2 32 4 53 6 74 8 9>>> >>> m = obj % 3 == 0>>> >>> obj.mask(m, -obj) A B0 0 11 2 -32 4 53 -6 74 8 -9>>> >>> obj.where(m, -obj) == obj.mask(~m, -obj) A B0 True True1 True True2 True True3 True True4 True True 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106900748未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Pandas","slug":"Python-数据分析/Pandas","permalink":"https://www.itrhx.com/categories/Python-数据分析/Pandas/"}],"tags":[{"name":"Pandas","slug":"Pandas","permalink":"https://www.itrhx.com/tags/Pandas/"},{"name":"数据重塑","slug":"数据重塑","permalink":"https://www.itrhx.com/tags/数据重塑/"},{"name":"数据替换","slug":"数据替换","permalink":"https://www.itrhx.com/tags/数据替换/"}]},{"title":"Python 数据分析三剑客之 Pandas(七):合并数据集","slug":"A85-Pandas-07","date":"2020-06-21T13:04:21.174Z","updated":"2020-07-06T13:45:11.851Z","comments":true,"path":"2020/06/21/A85-Pandas-07/","link":"","permalink":"https://www.itrhx.com/2020/06/21/A85-Pandas-07/","excerpt":"","text":"Pandas 系列文章: Python 数据分析三剑客之 Pandas(一):认识 Pandas 及其 Series、DataFrame 对象 Python 数据分析三剑客之 Pandas(二):Index 索引对象以及各种索引操作 Python 数据分析三剑客之 Pandas(三):算术运算与缺失值的处理 Python 数据分析三剑客之 Pandas(四):函数应用、映射、排序和层级索引 Python 数据分析三剑客之 Pandas(五):统计计算与统计描述 Python 数据分析三剑客之 Pandas(六):GroupBy 数据分裂、应用与合并 Python 数据分析三剑客之 Pandas(七):合并数据集 Python 数据分析三剑客之 Pandas(八):数据重塑、重复数据处理与数据替换 Python 数据分析三剑客之 Pandas(九):时间序列 Python 数据分析三剑客之 Pandas(十):数据读写 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106830112未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【01x00】concatpandas.concat 可以沿着指定轴将多个对象堆叠到一起。 官方文档:https://pandas.pydata.org/docs/reference/api/pandas.concat.html 基本语法: 12345678910pandas.concat(objs: Union[Iterable[‘DataFrame’], Mapping[Optional[Hashable], ‘DataFrame’]], axis='0', join: str = \"'outer'\", ignore_index: bool = 'False', keys='None', levels='None', names='None', verify_integrity: bool = 'False', sort: bool = 'False', copy: bool = 'True') → ’DataFrame’ 12345678910pandas.concat(objs: Union[Iterable[FrameOrSeriesUnion], Mapping[Optional[Hashable], FrameOrSeriesUnion]], axis='0', join: str = \"'outer'\", ignore_index: bool = 'False', keys='None', levels='None', names='None', verify_integrity: bool = 'False', sort: bool = 'False', copy: bool = 'True') → FrameOrSeriesUnion 常用参数描述: 参数 描述 objs Series 或 DataFrame 对象的序列或映射,要合并的对象 axis 沿指定轴合并,0 or ‘index’,1 or ‘columns’,只有在 DataFrame 中才有 1 or 'columns’ join 如何处理其他轴(或多个轴)上的索引,可取值:‘inner’,‘outer’(默认值)‘outer’:当 axis = 0 时,列名相同的列会合并,其余列都保留(并集),空值填充;‘inner’:当 axis = 0 时,列名相同的列会合并,其余列都舍弃(交集) ignore_index bool 类型,连接后的值是否使用原索引值,如果为 True,则索引将会是 0, 1, …, n-1 keys 序列形式,默认 None,传递 keys 后,会构造一个层次索引,即 MultiIndex 对象,keys 为最外层索引 levels 用于构造 MultiIndex 的特定级别(唯一值)。未指定则将从键中推断出来 names 列表类型,为索引添加标签 verify_integrity bool 类型,是否检查合并后的索引有无重复项,设置为 True 若有重复项则会报错 sort 当 join='outer' 时对列索引进行排序。当 join='inner' 时此操作无效 合并两个 Series 对象: 123456789>>> import pandas as pd>>> obj1 = pd.Series(['a', 'b'])>>> obj2 = pd.Series(['c', 'd'])>>> pd.concat([obj1, obj2])0 a1 b0 c1 ddtype: object 设置 ignore_index=True,放弃原有的索引值: 123456789>>> import pandas as pd>>> obj1 = pd.Series(['a', 'b'])>>> obj2 = pd.Series(['c', 'd'])>>> pd.concat([obj1, obj2], ignore_index=True)0 a1 b2 c3 ddtype: object 设置 keys 参数,添加最外层的索引: 123456789>>> import pandas as pd>>> obj1 = pd.Series(['a', 'b'])>>> obj2 = pd.Series(['c', 'd'])>>> pd.concat([obj1, obj2], keys=['s1', 's2'])s1 0 a 1 bs2 0 c 1 ddtype: object 设置 names 参数,为索引添加标签: 12345678910>>> import pandas as pd>>> obj1 = pd.Series(['a', 'b'])>>> obj2 = pd.Series(['c', 'd'])>>> pd.concat([obj1, obj2], keys=['s1', 's2'], names=['Series name', 'Row ID'])Series name Row IDs1 0 a 1 bs2 0 c 1 ddtype: object 合并 DataFrame 对象: 12345678910111213141516171819>>> import pandas as pd>>> obj1 = pd.DataFrame([['a', 1], ['b', 2]], columns=['letter', 'number'])>>> obj2 = pd.DataFrame([['c', 3], ['d', 4]], columns=['letter', 'number'])>>> obj1 letter number0 a 11 b 2>>> >>> obj2 letter number0 c 31 d 4>>> >>> pd.concat([obj1, obj2]) letter number0 a 11 b 20 c 31 d 4 合并 DataFrame 对象,不存在的值将会被 NaN 填充: 12345678910111213141516171819>>> import pandas as pd>>> obj1 = pd.DataFrame([['a', 1], ['b', 2]], columns=['letter', 'number'])>>> obj2 = pd.DataFrame([['c', 3, 'cat'], ['d', 4, 'dog']], columns=['letter', 'number', 'animal'])>>> obj1 letter number0 a 11 b 2>>> >>> obj2 letter number animal0 c 3 cat1 d 4 dog>>> >>> pd.concat([obj1, obj2]) letter number animal0 a 1 NaN1 b 2 NaN0 c 3 cat1 d 4 dog 合并 DataFrame 对象,设置 join="inner" 不存在的列将会舍弃: 12345678910111213141516171819>>> import pandas as pd>>> obj1 = pd.DataFrame([['a', 1], ['b', 2]], columns=['letter', 'number'])>>> obj2 = pd.DataFrame([['c', 3, 'cat'], ['d', 4, 'dog']], columns=['letter', 'number', 'animal'])>>> obj1 letter number0 a 11 b 2>>> >>> obj2 letter number animal0 c 3 cat1 d 4 dog>>> >>> pd.concat([obj1, obj2], join=\"inner\") letter number0 a 11 b 20 c 31 d 4 合并 DataFrame 对象,设置 axis=1 沿 y 轴合并(增加列): 1234567891011121314151617>>> import pandas as pd>>> obj1 = pd.DataFrame([['a', 1], ['b', 2]], columns=['letter', 'number'])>>> obj2 = pd.DataFrame([['bird', 'polly'], ['monkey', 'george']], columns=['animal', 'name'])>>> obj1 letter number0 a 11 b 2>>> >>> obj2 animal name0 bird polly1 monkey george>>> >>> pd.concat([obj1, obj2], axis=1) letter number animal name0 a 1 bird polly1 b 2 monkey george 设置 verify_integrity=True ,检查新的索引是否有重复项,有重复项会报错: 123456789101112131415>>> import pandas as pd>>> obj1 = pd.DataFrame([1], index=['a'])>>> obj2 = pd.DataFrame([2], index=['a'])>>> obj1 0a 1>>> >>> obj2 0a 2>>> >>> pd.concat([obj1, obj2], verify_integrity=True)Traceback (most recent call last): ...ValueError: Indexes have overlapping values: ['a'] 设置 sort=True,会对列索引进行排序输出: 123456789101112131415161718>>> obj1 = pd.DataFrame([['a', 3], ['d', 2]], columns=['letter', 'number'])>>> obj2 = pd.DataFrame([['c', 1, 'cat'], ['b', 4, 'dog']], columns=['letter', 'number', 'animal'])>>> obj1 letter number0 a 31 d 2>>> >>> obj2 letter number animal0 c 1 cat1 b 4 dog>>> >>> pd.concat([obj1, obj2], sort=True) animal letter number0 NaN a 31 NaN d 20 cat c 11 dog b 4 【02x00】appendAppend 方法事实上是在一个 Series / DataFrame 对象后最追加另一个 Series / DataFrame 对象并返回一个新对象,不改变原对象的值。 基本语法: Series.append(self, to_append, ignore_index=False, verify_integrity=False) DataFrame.append(self, other, ignore_index=False, verify_integrity=False, sort=False) 官方文档: https://pandas.pydata.org/docs/reference/api/pandas.Series.append.html https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.append.html 参数描述: 参数 描述 to_append / other 要追加的数据 ignore_index bool 类型,连接后的值是否使用原索引值,如果为 True,则索引将会是 0, 1, …, n-1 verify_integrity bool 类型,是否检查合并后的索引有无重复项,设置为 True 若有重复项则会报错 sort bool 类型,是否对列索引(columns)进行排序,默认 False 合并 Series 对象: 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253>>> import pandas as pd>>> obj1 = pd.Series([1, 2, 3])>>> obj2 = pd.Series([4, 5, 6])>>> obj3 = pd.Series([4, 5, 6], index=[3, 4, 5])>>> obj10 11 22 3dtype: int64>>> >>> obj20 41 52 6dtype: int64>>> >>> obj33 44 55 6dtype: int64>>> >>> obj1.append(obj2)0 11 22 30 41 52 6dtype: int64>>> >>> obj1.append(obj3)0 11 22 33 44 55 6dtype: int64>>> >>> obj1.append(obj2, ignore_index=True)0 11 22 33 44 55 6dtype: int64>>> >>> obj1.append(obj2, verify_integrity=True)Traceback (most recent call last):...ValueError: Indexes have overlapping values: Int64Index([0, 1, 2], dtype='int64') 合并 DataFrame 对象: 123456789101112131415161718192021222324252627>>> import pandas as pd>>> obj1 = pd.DataFrame([[1, 2], [3, 4]], columns=list('AB'))>>> obj2 = pd.DataFrame([[5, 6], [7, 8]], columns=list('AB'))>>> >>> obj1 A B0 1 21 3 4>>> >>> obj2 A B0 5 61 7 8>>> >>> obj1.append(obj2) A B0 1 21 3 40 5 61 7 8>>> >>> obj1.append(obj2, ignore_index=True) A B0 1 21 3 42 5 63 7 8 以下虽然不是生成 DataFrames 的推荐方法,但演示了从多个数据源生成 DataFrames 的两种方法: 12345678910111213>>> import pandas as pd>>> obj = pd.DataFrame(columns=['A'])>>> for i in range(5): obj = obj.append({'A': i}, ignore_index=True) >>> obj A0 01 12 23 34 4 12345678>>> import pandas as pd>>> pd.concat([pd.DataFrame([i], columns=['A']) for i in range(5)], ignore_index=True) A0 01 12 23 34 4 【03x00】merge将不同的数据源进行合并是数据科学中常见的操作,这既包括将两个不同的数据集非常简单地拼接在一起,也包括用数据库那样的连接(join)与合并(merge)操作处理有重叠字段的数据集。Series 与DataFrame 都具备这类操作,Pandas 的函数与方法让数据合并变得快速简单。 数据集的合并(merge)或连接(join)运算是通过一个或多个键将行连接起来的。这些运算是关系型数据库(基于SQL)的核心。Pandas 的 merge 函数是对数据应用这些算法的主要切入点。 pandas.merge 可根据一个或多个连接键将不同 DataFrame 中的行连接起来。 基本语法: 12345678910111213pandas.merge(left, right, how: str = 'inner', on=None, left_on=None, right_on=None, left_index: bool = False, right_index: bool = False, sort: bool = False, suffixes='_x', '_y', copy: bool = True, indicator: bool = False, validate=None) → ’DataFrame’ 官方文档:https://pandas.pydata.org/docs/reference/api/pandas.merge.html 常见参数描述: 参数 描述 left 参与合并的左侧 DataFrame 对象 right 参与合并的右侧 DataFrame 对象 how 合并方式,默认 'inner''inner':内连接,即使用两个对象中都有的键(交集);'outer':外连接,即使用两个对象中所有的键(并集);'left':左连接,即使用左对象中所有的键;'right':右连接,即使用右对象中所有的键; on 用于连接的列名。必须存在于左右两个 Dataframe对象中如果未指定,且其他连接键也未指定,则以 left 和 right 列名的交集作为连接键 left_on 左侧 DataFrame 对象中用作连接键的列 right_on 右侧 DataFrame 对象中用作连接键的列 left_index bool 类型,是否使用左侧 DataFrame 对象中的索引(index)作为连接键,默认 False right_index bool 类型,是否使用右侧 DataFrame 对象中的索引(index)作为连接键,默认 False sort bool 类型,是否在结果中按顺序对连接键排序,默认 False。如果为 False,则连接键的顺序取决于联接类型(how 关键字) suffixes 字符串值元组,用于追加到重叠列名的末尾,默认为 ('_x', '_y')。例如,如果左右两个 DataFrame 对象都有 data 列时,则结果中就会出现 data_x 和 data_y 【03x01】一对一连接一对一连接是指两个 DataFrame 对象的列的值没有重复值。 如果不指定任何参数,调用 merge 方法,merge 就会将重叠的列的列名当做键来合并。 在下面的示例中,两个 DataFrame 对象都有一个列名为 key 的列,未指定按照哪一列来合并,merge 就会默认按照 key 来合并: 1234567891011121314151617181920>>> import pandas as pd>>> obj1 = pd.DataFrame({'key': ['b', 'a', 'c'], 'data1': range(3)})>>> obj2 = pd.DataFrame({'key': ['a', 'c', 'b'], 'data2': range(3)})>>> obj1 key data10 b 01 a 12 c 2>>> >>> obj2 key data20 a 01 c 12 b 2>>> >>> pd.merge(obj1, obj2) key data1 data20 b 0 21 a 1 02 c 2 1 【03x02】多对一连接多对一连接是指两个 DataFrame 对象中,有一个的列的值有重复值。通过多对一连接获得的结果,DataFrame 将会保留重复值。12345678910111213141516171819202122232425262728>>> import pandas as pd>>> obj1 = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'a', 'b'], 'data1': range(7)})>>> obj2 = pd.DataFrame({'key': ['a', 'b', 'd'], 'data2': range(3)})>>> >>> obj1 key data10 b 01 b 12 a 23 c 34 a 45 a 56 b 6>>> >>> obj2 key data20 a 01 b 12 d 2>>> >>> pd.merge(obj1, obj2) key data1 data20 b 0 11 b 1 12 b 6 13 a 2 04 a 4 05 a 5 0### 【03x03】多对多连接 多对多连接是指两个 DataFrame 对象中的列的值都有重复值。 1234567891011121314151617181920212223242526272829>>> import pandas as pd>>> obj1 = pd.DataFrame({'key': ['a', 'b', 'b', 'c'], 'data1': range(4)})>>> obj2 = pd.DataFrame({'key': ['a', 'a', 'b', 'b', 'c', 'c'], 'data2': range(6)})>>> obj1 key data10 a 01 b 12 b 23 c 3>>> >>> obj2 key data20 a 01 a 12 b 23 b 34 c 45 c 5>>> >>> pd.merge(obj1, obj2) key data1 data20 a 0 01 a 0 12 b 1 23 b 1 34 b 2 25 b 2 36 c 3 47 c 3 5 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106830112未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【03x04】参数 on / left_on / right_on参数 on 用于指定按照某一列来进行合并,若不指定该参数,则会默认按照重叠的列的列名当做键来合并: 1234567891011121314151617181920>>> import pandas as pd>>> obj1 = pd.DataFrame({'key': ['b', 'a', 'c'], 'data1': range(3)})>>> obj2 = pd.DataFrame({'key': ['a', 'c', 'b'], 'data2': range(3)})>>> obj1 key data10 b 01 a 12 c 2>>> >>> obj2 key data20 a 01 c 12 b 2>>> >>> pd.merge(obj1, obj2, on='key') key data1 data20 b 0 21 a 1 02 c 2 1 如果要根据多个键进行合并,传入一个由列名组成的列表即可: 12345678910111213141516171819202122232425>>> import pandas as pd>>> left = pd.DataFrame({'key1': ['foo', 'foo', 'bar'], 'key2': ['one', 'two', 'one'], 'lval': [1, 2, 3]})>>> right = pd.DataFrame({'key1': ['foo', 'foo', 'bar', 'bar'], 'key2': ['one', 'one', 'one', 'two'], 'rval': [4, 5, 6, 7]})>>> left key1 key2 lval0 foo one 11 foo two 22 bar one 3>>> >>> right key1 key2 rval0 foo one 41 foo one 52 bar one 63 bar two 7>>> >>> pd.merge(left, right, on=['key1', 'key2']) key1 key2 lval rval0 foo one 1 41 foo one 1 52 bar one 3 6 如果两个对象的列名不同,就可以使用 left_on、right_on 参数分别进行指定: 123456789101112131415161718192021222324252627>>> import pandas as pd>>> obj1 = pd.DataFrame({'lkey': ['b', 'b', 'a', 'c', 'a', 'a', 'b'], 'data1': range(7)})>>> obj2 = pd.DataFrame({'rkey': ['a', 'b', 'd'], 'data2': range(3)})>>> obj1 lkey data10 b 01 b 12 a 23 c 34 a 45 a 56 b 6>>> >>> obj2 rkey data20 a 01 b 12 d 2>>> >>> pd.merge(obj1, obj2, left_on='lkey', right_on='rkey') lkey data1 rkey data20 b 0 b 11 b 1 b 12 b 6 b 13 a 2 a 04 a 4 a 05 a 5 a 0 【03x05】参数 how在前面的示例中,结果里面 c 和 d 以及与之相关的数据消失了。默认情况下,merge 做的是内连接('inner'),结果中的键是交集。其他方式还有:'left'、'right'、'outer',含义如下: 'inner':内连接,即使用两个对象中都有的键(交集); 'outer':外连接,即使用两个对象中所有的键(并集); 'left':左连接,即使用左对象中所有的键; 'right':右连接,即使用右对象中所有的键; 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758>>> import pandas as pd>>> obj1 = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'a', 'b'], 'data1': range(7)})>>> obj2 = pd.DataFrame({'key': ['a', 'b', 'd'], 'data2': range(3)})>>> obj1 key data10 b 01 b 12 a 23 c 34 a 45 a 56 b 6>>> >>> obj2 key data20 a 01 b 12 d 2>>> >>> pd.merge(obj1, obj2, on='key', how='inner') key data1 data20 b 0 11 b 1 12 b 6 13 a 2 04 a 4 05 a 5 0>>> >>> pd.merge(obj1, obj2, on='key', how='outer') key data1 data20 b 0.0 1.01 b 1.0 1.02 b 6.0 1.03 a 2.0 0.04 a 4.0 0.05 a 5.0 0.06 c 3.0 NaN7 d NaN 2.0>>> >>> pd.merge(obj1, obj2, on='key', how='left') key data1 data20 b 0 1.01 b 1 1.02 a 2 0.03 c 3 NaN4 a 4 0.05 a 5 0.06 b 6 1.0>>> >>> pd.merge(obj1, obj2, on='key', how='right') key data1 data20 b 0.0 11 b 1.0 12 b 6.0 13 a 2.0 04 a 4.0 05 a 5.0 06 d NaN 2 【03x06】参数 suffixessuffixes 参数用于指定附加到左右两个 DataFrame 对象的重叠列名上的字符串: 在以下示例中,选择按照 key1 进行合并,而两个 DataFrame 对象都包含 key2 列,如果未指定 suffixes 参数,则默认会为两个对象的 key2 加上 _x 和 _y,以便区分它们,如果指定了 suffixes 参数,就会按照添加指定的后缀: 12345678910111213141516171819202122232425262728293031323334353637>>> import pandas as pd>>> left = pd.DataFrame({'key1': ['foo', 'foo', 'bar'], 'key2': ['one', 'two', 'one'], 'lval': [1, 2, 3]})>>> right = pd.DataFrame({'key1': ['foo', 'foo', 'bar', 'bar'], 'key2': ['one', 'one', 'one', 'two'], 'rval': [4, 5, 6, 7]})>>> left key1 key2 lval0 foo one 11 foo two 22 bar one 3>>> >>> right key1 key2 rval0 foo one 41 foo one 52 bar one 63 bar two 7>>> >>> pd.merge(left, right, on='key1') key1 key2_x lval key2_y rval0 foo one 1 one 41 foo one 1 one 52 foo two 2 one 43 foo two 2 one 54 bar one 3 one 65 bar one 3 two 7>>> >>> pd.merge(left, right, on='key1', suffixes=('_left', '_right')) key1 key2_left lval key2_right rval0 foo one 1 one 41 foo one 1 one 52 foo two 2 one 43 foo two 2 one 54 bar one 3 one 65 bar one 3 two 7 【03x07】参数 left_index / right_index有时候,DataFrame 中的连接键位于其索引中。在这种情况下,可以使用 left_index=True 或right_index=True(或两个都传)以说明索引应该被用作连接键。这种方法称为按索引连接,在 Pandas 中还有个 join 方法可以实现这个功能。 在以下示例中,按照 left 的 key 列进行连接,而 right 对象的连接键位于其索引中,因此要指定 right_index=True: 123456789101112131415161718192021222324>>> import pandas as pd>>> left = pd.DataFrame({'key': ['a', 'b', 'a', 'a', 'b', 'c'], 'value': range(6)})>>> right = pd.DataFrame({'group_val': [3.5, 7]}, index=['a', 'b'])>>> left key value0 a 01 b 12 a 23 a 34 b 45 c 5>>> >>> right group_vala 3.5b 7.0>>> >>> pd.merge(left, right, left_on='key', right_index=True) key value group_val0 a 0 3.52 a 2 3.53 a 3 3.51 b 1 7.04 b 4 7.0 【04x00】joinjoin 方法只适用于 DataFrame 对象,Series 对象没有该方法,该方法用于连接另一个 DataFrame 对象的列(columns)。 基本语法:DataFrame.join(self, other, on=None, how='left', lsuffix='', rsuffix='', sort=False) → ’DataFrame’ 参数描述: 参数 描述 other 另一个 DataFrame、Series 或 DataFrame 列表对象 on 列名称,或者列名称组成的列表、元组,连接的列 how 合并方式,默认 'left''inner':内连接,即使用两个对象中都有的键(交集);'outer':外连接,即使用两个对象中所有的键(并集);'left':左连接,即使用左对象中所有的键;'right':右连接,即使用右对象中所有的键; lsuffix 当两个对象有相同的列名时,合并后左边数据列名的后缀 rsuffix 当两个对象有相同的列名时,合并后右边数据列名的后缀 sort bool 类型,是否在结果中按顺序对连接键排序,默认 False。如果为 False,则连接键的顺序取决于联接类型(how 关键字) 使用 lsuffix 和 rsuffix 参数: 12345678910111213141516171819202122232425262728>>> import pandas as pd>>> obj = pd.DataFrame({'key': ['K0', 'K1', 'K2', 'K3', 'K4', 'K5'], 'A': ['A0', 'A1', 'A2', 'A3', 'A4', 'A5']})>>> other = pd.DataFrame({'key': ['K0', 'K1', 'K2'], 'B': ['B0', 'B1', 'B2']})>>> obj key A0 K0 A01 K1 A12 K2 A23 K3 A34 K4 A45 K5 A5>>> >>> other key B0 K0 B01 K1 B12 K2 B2>>> >>> obj.join(other, lsuffix='_1', rsuffix='_2') key_1 A key_2 B0 K0 A0 K0 B01 K1 A1 K1 B12 K2 A2 K2 B23 K3 A3 NaN NaN4 K4 A4 NaN NaN5 K5 A5 NaN NaN 如果右表的索引是左表的某一列的值,这时可以将右表的索引和左表的列对齐合并这样的灵活方式进行合并: 123456789101112131415161718192021>>> import pandas as pd>>> obj = pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'], 'B': ['B0', 'B1', 'B2', 'B3'],'key': ['K0', 'K1', 'K0', 'K1']})>>> other = pd.DataFrame({'C': ['C0', 'C1'],'D': ['D0', 'D1']},index=['K0', 'K1'])>>> obj A B key0 A0 B0 K01 A1 B1 K12 A2 B2 K03 A3 B3 K1>>> >>> other C DK0 C0 D0K1 C1 D1>>> >>> obj.join(other, on='key') A B key C D0 A0 B0 K0 C0 D01 A1 B1 K1 C1 D12 A2 B2 K0 C0 D03 A3 B3 K1 C1 D1 【05x00】四种方法的区别 concat:可用于两个或多个 Series 或 DataFrame 对象间,通过 axis 参数指定按照行方向(增加行)或列方向(增加列)进合并操作,默认行合并(增加行),取并集; append:在一个 Series 或 DataFrame 对象后最追加另一个 Series 或 DataFrame 对象并返回一个新对象,不改变原对象的值。只能按行合并(增加行)。 merge:只能对两个 DataFrame 对象进行合并,一般按照列方向(增加列)进行合并操作,按照行方向合并一般用 join 方法代替,默认列合并(增加列),取交集; join:只能对两个 DataFrame 对象进行合并,按照列方向(增加列)进行合并操作,默认左连接。 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106830112未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Pandas","slug":"Python-数据分析/Pandas","permalink":"https://www.itrhx.com/categories/Python-数据分析/Pandas/"}],"tags":[{"name":"Pandas","slug":"Pandas","permalink":"https://www.itrhx.com/tags/Pandas/"},{"name":"合并数据集","slug":"合并数据集","permalink":"https://www.itrhx.com/tags/合并数据集/"}]},{"title":"Python 数据分析三剑客之 Pandas(六):GroupBy 数据分裂/应用/合并","slug":"A84-Pandas-06","date":"2020-06-17T15:58:49.204Z","updated":"2020-08-07T01:13:45.027Z","comments":true,"path":"2020/06/17/A84-Pandas-06/","link":"","permalink":"https://www.itrhx.com/2020/06/17/A84-Pandas-06/","excerpt":"","text":"Pandas 系列文章: Python 数据分析三剑客之 Pandas(一):认识 Pandas 及其 Series、DataFrame 对象 Python 数据分析三剑客之 Pandas(二):Index 索引对象以及各种索引操作 Python 数据分析三剑客之 Pandas(三):算术运算与缺失值的处理 Python 数据分析三剑客之 Pandas(四):函数应用、映射、排序和层级索引 Python 数据分析三剑客之 Pandas(五):统计计算与统计描述 Python 数据分析三剑客之 Pandas(六):GroupBy 数据分裂、应用与合并 Python 数据分析三剑客之 Pandas(七):合并数据集 Python 数据分析三剑客之 Pandas(八):数据重塑、重复数据处理与数据替换 Python 数据分析三剑客之 Pandas(九):时间序列 Python 数据分析三剑客之 Pandas(十):数据读写 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106804881未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【01x00】GroupBy 机制对数据集进行分组并对各组应用一个函数(无论是聚合还是转换),通常是数据分析工作中的重要环节。在将数据集加载、融合、准备好之后,通常就是计算分组统计或生成透视表。Pandas 提供了一个灵活高效的 GroupBy 功能,虽然“分组”(group by)这个名字是借用 SQL 数据库语言的命令,但其理念引用发明 R 语言 frame 的 Hadley Wickham 的观点可能更合适:分裂(Split)、应用(Apply)和组合(Combine)。 分组运算过程:Split —> Apply —> Combine 分裂(Split):根据某些标准将数据分组; 应用(Apply):对每个组独立应用一个函数; 合并(Combine):把每个分组的计算结果合并起来。 官方介绍:https://pandas.pydata.org/docs/user_guide/groupby.html 【02x00】GroupBy 对象常见的 GroupBy 对象:Series.groupby、DataFrame.groupby,基本语法如下: 123456789Series.groupby(self, by=None, axis=0, level=None, as_index: bool = True, sort: bool = True, group_keys: bool = True, squeeze: bool = False, observed: bool = False) → ’groupby_generic.SeriesGroupBy’ 123456789DataFrame.groupby(self, by=None, axis=0, level=None, as_index: bool = True, sort: bool = True, group_keys: bool = True, squeeze: bool = False, observed: bool = False) → ’groupby_generic.DataFrameGroupBy’ 官方文档: https://pandas.pydata.org/docs/reference/api/pandas.Series.groupby.html https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.groupby.html 常用参数解释如下: 参数 描述 by 映射、函数、标签或标签列表,用于确定分组依据的分组。如果 by 是函数,则会在对象索引的每个值上调用它。 如果传递了 dict 或 Series,则将使用 Series 或 dict 的值来确定组(将 Series 的值首先对齐;请参见.align() 方法)。 如果传递了 ndarray,则按原样使用这些值来确定组。标签或标签列表可以按自身中的列传递给分组。 注意,元组被解释为(单个)键 axis 沿指定轴拆分,默认 0,0 or ‘index’,1 or ‘columns’,只有在 DataFrame 中才有 1 or 'columns’ level 如果轴是 MultiIndex(层次结构),则按特定层级进行分组,默认 None as_index bool 类型,默认 True,对于聚合输出,返回以组标签为索引的对象。仅与 DataFrame 输入相关。as_index=False 实际上是“SQL样式”分组输出 sort bool 类型,默认 True,对组键排序。关闭此选项可获得更好的性能。注:这不影响每组的观察顺序。Groupby 保留每个组中行的顺序 group_keys bool 类型,默认 True,调用 apply 方法时,是否将组键(keys)添加到索引( index)以标识块 squeeze bool 类型,默认 False,如果可能,减少返回类型的维度,否则返回一致的类型 groupby() 进行分组,GroupBy 对象没有进行实际运算,只是包含分组的中间数据,示例如下: 123456789101112131415161718192021222324>>> import pandas as pd>>> import numpy as np>>> data = {'key1' : ['a', 'b', 'a', 'b', 'a', 'b', 'a', 'a'], 'key2' : ['one', 'one', 'two', 'three', 'two', 'two', 'one', 'three'], 'data1': np.random.randn(8), 'data2': np.random.randn(8)}>>> >>> obj = pd.DataFrame(data)>>> obj key1 key2 data1 data20 a one -0.804160 -0.8689051 b one -0.086990 0.3257412 a two 0.757992 0.5411013 b three -0.281435 0.0978414 a two 0.817757 -0.6436995 b two -0.462760 -0.3211966 a one -0.403699 0.6021387 a three 0.883940 -0.850526>>> >>> obj.groupby('key1')<pandas.core.groupby.generic.DataFrameGroupBy object at 0x03CDB7C0>>>> >>> obj['data1'].groupby(obj['key1'])<pandas.core.groupby.generic.SeriesGroupBy object at 0x03CDB748> 【03x00】GroupBy Split 数据分裂【03x01】分组运算前面通过 groupby() 方法获得了一个 GroupBy 对象,它实际上还没有进行任何计算,只是含有一些有关分组键 obj['key1'] 的中间数据而已。换句话说,该对象已经有了接下来对各分组执行运算所需的一切信息。例如,我们可以调用 GroupBy 的 mean() 方法来计算分组平均值,size() 方法返回每个分组的元素个数: 123456789101112131415161718192021222324252627282930313233343536373839404142434445>>> import pandas as pd>>> import numpy as np>>> data = {'key1' : ['a', 'b', 'a', 'b', 'a', 'b', 'a', 'a'], 'key2' : ['one', 'one', 'two', 'three', 'two', 'two', 'one', 'three'], 'data1': np.random.randn(8), 'data2': np.random.randn(8)}>>> >>> obj = pd.DataFrame(data)>>> obj key1 key2 data1 data20 a one -0.544099 -0.6140791 b one 2.193712 0.1010052 a two -0.004683 0.8827703 b three 0.312858 1.7321054 a two 0.011089 0.0895875 b two 0.292165 1.3276386 a one -1.433291 -0.2389717 a three -0.004724 -2.117326>>> >>> grouped1 = obj.groupby('key1')>>> grouped2 = obj['data1'].groupby(obj['key1'])>>> >>> grouped1.mean() data1 data2key1 a -0.395142 -0.399604b 0.932912 1.053583>>> >>> grouped2.mean()key1a -0.395142b 0.932912Name: data1, dtype: float64>>>>>> grouped1.size()key1a 5b 3dtype: int64>>> >>> grouped2.size()key1a 5b 3Name: data1, dtype: int64 【03x02】按类型按列分组groupby() 方法 axis 参数默认是 0,通过设置也可以在其他任何轴上进行分组,也支持按照类型(dtype)进行分组: 12345678910111213141516171819202122232425262728293031323334353637383940>>> import pandas as pd>>> import numpy as np>>> data = {'key1' : ['a', 'b', 'a', 'b', 'a', 'b', 'a', 'a'], 'key2' : ['one', 'one', 'two', 'three', 'two', 'two', 'one', 'three'], 'data1': np.random.randn(8), 'data2': np.random.randn(8)}>>> obj = pd.DataFrame(data)>>> obj key1 key2 data1 data20 a one -0.607009 1.9483011 b one 0.150818 -0.0250952 a two -2.086024 0.3581643 b three 0.446061 1.7087974 a two 0.745457 -0.9809485 b two 0.981877 2.1593276 a one 0.804480 -0.4996617 a three 0.112884 0.004367>>> >>> obj.dtypeskey1 objectkey2 objectdata1 float64data2 float64dtype: object>>> >>> obj.groupby(obj.dtypes, axis=1).size()float64 2object 2dtype: int64>>> >>> obj.groupby(obj.dtypes, axis=1).sum() float64 object0 1.341291 aone1 0.125723 bone2 -1.727860 atwo3 2.154858 bthree4 -0.235491 atwo5 3.141203 btwo6 0.304819 aone7 0.117251 athree 【03x03】自定义分组groupby() 方法中可以一次传入多个数组的列表,也可以自定义一组分组键。也可以通过一个字典、一个函数,或者按照索引层级进行分组。 传入多个数组的列表: 12345678910111213141516171819202122232425262728293031323334>>> import pandas as pd>>> import numpy as np>>> data = {'key1' : ['a', 'b', 'a', 'b', 'a', 'b', 'a', 'a'], 'key2' : ['one', 'one', 'two', 'three', 'two', 'two', 'one', 'three'], 'data1': np.random.randn(8), 'data2': np.random.randn(8)}>>> obj = pd.DataFrame(data)>>> obj key1 key2 data1 data20 a one -0.841652 0.6880551 b one 0.510042 -0.5611712 a two -0.418862 -0.1459833 b three -1.104698 0.5631584 a two 0.329527 -0.8931085 b two 0.753653 -0.3425206 a one -0.882527 -1.1213297 a three 1.726794 0.160244>>> >>> means = obj['data1'].groupby([obj['key1'], obj['key2']]).mean()>>> meanskey1 key2 a one -0.862090 three 1.726794 two -0.044667b one 0.510042 three -1.104698 two 0.753653Name: data1, dtype: float64>>> >>> means.unstack()key2 one three twokey1 a -0.862090 1.726794 -0.044667b 0.510042 -1.104698 0.753653 自定义分组键: 1234567891011121314151617181920212223>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame({'key1' : ['a', 'a', 'b', 'b', 'a'], 'key2' : ['one', 'two', 'one', 'two', 'one'], 'data1' : np.random.randn(5), 'data2' : np.random.randn(5)})>>> obj key1 key2 data1 data20 a one -0.024003 0.3504801 a two -0.767534 -0.1004262 b one -0.594983 -1.9455803 b two -0.374482 0.8175924 a one 0.755452 -0.137759>>> >>> states = np.array(['Wuhan', 'Beijing', 'Beijing', 'Wuhan', 'Wuhan'])>>> years = np.array([2005, 2005, 2006, 2005, 2006])>>> >>> obj['data1'].groupby([states, years]).mean()Beijing 2005 -0.767534 2006 -0.594983Wuhan 2005 -0.199242 2006 0.755452Name: data1, dtype: float64 【03x03x01】字典分组通过字典进行分组: 1234567891011121314151617181920212223242526272829303132333435>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame(np.random.randint(1, 10, (5,5)), columns=['a', 'b', 'c', 'd', 'e'], index=['A', 'B', 'C', 'D', 'E'])>>> obj a b c d eA 1 4 7 1 9B 8 2 4 7 8C 9 8 2 5 1D 2 4 2 8 3E 7 5 7 2 3>>> >>> obj_dict = {'a':'Python', 'b':'Python', 'c':'Java', 'd':'C++', 'e':'Java'}>>> obj.groupby(obj_dict, axis=1).size()C++ 1Java 2Python 2dtype: int64>>> >>> obj.groupby(obj_dict, axis=1).count() C++ Java PythonA 1 2 2B 1 2 2C 1 2 2D 1 2 2E 1 2 2>>> >>> obj.groupby(obj_dict, axis=1).sum() C++ Java PythonA 1 16 5B 7 12 10C 5 3 17D 8 5 6E 2 10 12 【03x03x02】函数分组通过函数进行分组: 123456789101112131415161718192021222324>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame(np.random.randint(1, 10, (5,5)), columns=['a', 'b', 'c', 'd', 'e'], index=['AA', 'BBB', 'CC', 'D', 'EE'])>>> obj a b c d eAA 3 9 5 8 2BBB 1 4 2 2 6CC 9 2 4 7 6D 2 5 5 7 1EE 8 8 8 2 2>>> >>> def group_key(idx): \"\"\" idx 为列索引或行索引 \"\"\" return len(idx)>>> obj.groupby(group_key).size() # 等价于 obj.groupby(len).size()1 12 33 1dtype: int64 【03x03x03】索引层级分组通过不同索引层级进行分组: 1234567891011121314151617181920212223242526272829>>> import pandas as pd>>> import numpy as np>>> columns = pd.MultiIndex.from_arrays([['Python', 'Java', 'Python', 'Java', 'Python'], ['A', 'A', 'B', 'C', 'B']], names=['language', 'index'])>>> obj = pd.DataFrame(np.random.randint(1, 10, (5, 5)), columns=columns)>>> objlanguage Python Java Python Java Pythonindex A A B C B0 7 1 9 8 51 4 5 4 5 62 4 3 1 9 53 6 6 3 8 14 7 9 2 8 2>>> >>> obj.groupby(level='language', axis=1).sum()language Java Python0 9 211 10 142 12 103 14 104 17 11>>> >>> obj.groupby(level='index', axis=1).sum()index A B C0 8 14 81 9 10 52 7 6 93 12 4 84 16 4 8 【03x04】分组迭代GroupBy 对象支持迭代,对于单层分组,可以产生一组二元元组,由分组名和数据块组成: 1234567891011121314151617181920212223242526272829303132333435>>> import pandas as pd>>> import numpy as np>>> data = {'key1' : ['a', 'b', 'a', 'b', 'a', 'b', 'a', 'a'], 'key2' : ['one', 'one', 'two', 'three', 'two', 'two', 'one', 'three'], 'data1': np.random.randn(8), 'data2': np.random.randn(8)}>>> obj = pd.DataFrame(data)>>> obj key1 key2 data1 data20 a one -1.088762 0.6685041 b one 0.275500 0.7878442 a two -0.108417 -0.4912963 b three 0.019524 -0.3633904 a two 0.453612 0.7969995 b two 1.982858 1.5018776 a one 1.101132 -1.9283627 a three 0.524775 -1.205842>>> >>> for group_name, group_data in obj.groupby('key1'): print(group_name) print(group_data) a key1 key2 data1 data20 a one -1.088762 0.6685042 a two -0.108417 -0.4912964 a two 0.453612 0.7969996 a one 1.101132 -1.9283627 a three 0.524775 -1.205842b key1 key2 data1 data21 b one 0.275500 0.7878443 b three 0.019524 -0.3633905 b two 1.982858 1.501877 对于多层分组,元组的第一个元素将会是由键值组成的元组,第二个元素为数据块: 12345678910111213141516171819202122232425262728293031323334353637383940414243>>> import pandas as pd>>> import numpy as np>>> data = {'key1' : ['a', 'b', 'a', 'b', 'a', 'b', 'a', 'a'], 'key2' : ['one', 'one', 'two', 'three', 'two', 'two', 'one', 'three'], 'data1': np.random.randn(8), 'data2': np.random.randn(8)}>>> obj = pd.DataFrame(data)>>> obj key1 key2 data1 data20 a one -1.088762 0.6685041 b one 0.275500 0.7878442 a two -0.108417 -0.4912963 b three 0.019524 -0.3633904 a two 0.453612 0.7969995 b two 1.982858 1.5018776 a one 1.101132 -1.9283627 a three 0.524775 -1.205842>>> >>> for group_name, group_data in obj.groupby(['key1', 'key2']): print(group_name) print(group_data) ('a', 'one') key1 key2 data1 data20 a one -1.088762 0.6685046 a one 1.101132 -1.928362('a', 'three') key1 key2 data1 data27 a three 0.524775 -1.205842('a', 'two') key1 key2 data1 data22 a two -0.108417 -0.4912964 a two 0.453612 0.796999('b', 'one') key1 key2 data1 data21 b one 0.2755 0.787844('b', 'three') key1 key2 data1 data23 b three 0.019524 -0.36339('b', 'two') key1 key2 data1 data25 b two 1.982858 1.501877 【03x05】对象转换GroupBy 对象支持转换成列表或字典: 123456789101112131415161718192021222324252627282930313233343536373839404142>>> import pandas as pd>>> import numpy as np>>> data = {'key1' : ['a', 'b', 'a', 'b', 'a', 'b', 'a', 'a'], 'key2' : ['one', 'one', 'two', 'three', 'two', 'two', 'one', 'three'], 'data1': np.random.randn(8), 'data2': np.random.randn(8)}>>> obj = pd.DataFrame(data)>>> obj key1 key2 data1 data20 a one -0.607009 1.9483011 b one 0.150818 -0.0250952 a two -2.086024 0.3581643 b three 0.446061 1.7087974 a two 0.745457 -0.9809485 b two 0.981877 2.1593276 a one 0.804480 -0.4996617 a three 0.112884 0.004367>>> >>> grouped = obj.groupby('key1')>>> list(grouped)[('a', key1 key2 data1 data20 a one -0.607009 1.9483012 a two -2.086024 0.3581644 a two 0.745457 -0.9809486 a one 0.804480 -0.4996617 a three 0.112884 0.004367),('b', key1 key2 data1 data21 b one 0.150818 -0.0250953 b three 0.446061 1.7087975 b two 0.981877 2.159327)]>>>>>> dict(list(grouped)){'a': key1 key2 data1 data20 a one -0.607009 1.9483012 a two -2.086024 0.3581644 a two 0.745457 -0.9809486 a one 0.804480 -0.4996617 a three 0.112884 0.004367,'b': key1 key2 data1 data21 b one 0.150818 -0.0250953 b three 0.446061 1.7087975 b two 0.981877 2.159327} 【04x00】GroupBy Apply 数据应用聚合指的是任何能够从数组产生标量值的数据转换过程,常用于对分组后的数据进行计算 【04x01】聚合函数之前的例子已经用过一些内置的聚合函数,比如 mean、count、min 以及 sum 等。常见的聚合运算如下表所示: 官方文档:https://pandas.pydata.org/docs/reference/groupby.html 方法 描述 count 非NA值的数量 describe 针对Series或各DataFrame列计算汇总统计 min 计算最小值 max 计算最大值 argmin 计算能够获取到最小值的索引位置(整数) argmax 计算能够获取到最大值的索引位置(整数) idxmin 计算能够获取到最小值的索引值 idxmax 计算能够获取到最大值的索引值 quantile 计算样本的分位数(0到1) sum 值的总和 mean 值的平均数 median 值的算术中位数(50%分位数) mad 根据平均值计算平均绝对离差 var 样本值的方差 std 样本值的标准差 应用示例: 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162>>> import pandas as pd>>> import numpy as np>>> obj = {'key1' : ['a', 'b', 'a', 'b', 'a', 'b', 'a', 'a'], 'key2' : ['one', 'one', 'two', 'three', 'two', 'two', 'one', 'three'], 'data1': np.random.randint(1,10, 8), 'data2': np.random.randint(1,10, 8)}>>> obj = pd.DataFrame(obj)>>> obj key1 key2 data1 data20 a one 9 71 b one 5 92 a two 2 43 b three 3 44 a two 5 15 b two 5 96 a one 1 87 a three 2 4>>> >>> obj.groupby('key1').sum() data1 data2key1 a 19 24b 13 22>>> >>> obj.groupby('key1').max() key2 data1 data2key1 a two 9 8b two 5 9>>> >>> obj.groupby('key1').min() key2 data1 data2key1 a one 1 1b one 3 4>>> >>> obj.groupby('key1').mean() data1 data2key1 a 3.800000 4.800000b 4.333333 7.333333>>> >>> obj.groupby('key1').size()key1a 5b 3dtype: int64>>> >>> obj.groupby('key1').count() key2 data1 data2key1 a 5 5 5b 3 3 3>>> >>> obj.groupby('key1').describe() data1 ... data2 count mean std min 25% ... min 25% 50% 75% maxkey1 ... a 5.0 3.800000 3.271085 1.0 2.0 ... 1.0 4.0 4.0 7.0 8.0b 3.0 4.333333 1.154701 3.0 4.0 ... 4.0 6.5 9.0 9.0 9.0[2 rows x 16 columns] 【04x02】自定义函数如果自带的内置函数满足不了我们的要求,则可以自定义一个聚合函数,然后传入 GroupBy.agg(func) 或 GroupBy.aggregate(func) 方法中即可。func 的参数为 groupby 索引对应的记录。 123456789101112131415161718192021222324252627282930313233>>> import pandas as pd>>> import numpy as np>>> obj = {'key1' : ['a', 'b', 'a', 'b', 'a', 'b', 'a', 'a'], 'key2' : ['one', 'one', 'two', 'three', 'two', 'two', 'one', 'three'], 'data1': np.random.randint(1,10, 8), 'data2': np.random.randint(1,10, 8)}>>> obj = pd.DataFrame(obj)>>> obj key1 key2 data1 data20 a one 9 71 b one 5 92 a two 2 43 b three 3 44 a two 5 15 b two 5 96 a one 1 87 a three 2 4>>> >>> def peak_range(df): return df.max() - df.min()>>> >>> obj.groupby('key1').agg(peak_range) data1 data2key1 a 8 7b 2 5>>> >>> obj.groupby('key1').agg(lambda df : df.max() - df.min()) data1 data2key1 a 8 7b 2 5 【04x03】对不同列作用不同函数使用字典可以对不同列作用不同的聚合函数: 123456789101112131415161718192021222324252627282930313233>>> import pandas as pd>>> import numpy as np>>> obj = {'key1' : ['a', 'b', 'a', 'b', 'a', 'b', 'a', 'a'], 'key2' : ['one', 'one', 'two', 'three', 'two', 'two', 'one', 'three'], 'data1': np.random.randint(1,10, 8), 'data2': np.random.randint(1,10, 8)}>>> obj = pd.DataFrame(obj)>>> obj key1 key2 data1 data20 a one 9 71 b one 5 92 a two 2 43 b three 3 44 a two 5 15 b two 5 96 a one 1 87 a three 2 4>>> >>> dict1 = {'data1':'mean', 'data2':'sum'}>>> dict2 = {'data1':['mean','max'], 'data2':'sum'}>>> >>> obj.groupby('key1').agg(dict1) data1 data2key1 a 3.800000 24b 4.333333 22>>> >>> obj.groupby('key1').agg(dict2) data1 data2 mean max sumkey1 a 3.800000 9 24b 4.333333 5 22 【04x04】GroupBy.apply()apply() 方法会将待处理的对象拆分成多个片段,然后对各片段调用传入的函数,最后尝试将各片段组合到一起。 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960>>> import pandas as pd>>> obj = pd.DataFrame({'A':['bob','sos','bob','sos','bob','sos','bob','bob'], 'B':['one','one','two','three','two','two','one','three'], 'C':[3,1,4,1,5,9,2,6], 'D':[1,2,3,4,5,6,7,8]})>>> obj A B C D0 bob one 3 11 sos one 1 22 bob two 4 33 sos three 1 44 bob two 5 55 sos two 9 66 bob one 2 77 bob three 6 8>>> >>> grouped = obj.groupby('A')>>> for name, group in grouped: print(name) print(group) bob A B C D0 bob one 3 12 bob two 4 34 bob two 5 56 bob one 2 77 bob three 6 8sos A B C D1 sos one 1 23 sos three 1 45 sos two 9 6>>> >>> grouped.apply(lambda x:x.describe()) # 对 bob 和 sos 两组数据使用 describe 方法 C DA bob count 5.000000 5.000000 mean 4.000000 4.800000 std 1.581139 2.863564 min 2.000000 1.000000 25% 3.000000 3.000000 50% 4.000000 5.000000 75% 5.000000 7.000000 max 6.000000 8.000000sos count 3.000000 3.000000 mean 3.666667 4.000000 std 4.618802 2.000000 min 1.000000 2.000000 25% 1.000000 3.000000 50% 1.000000 4.000000 75% 5.000000 5.000000 max 9.000000 6.000000>>>>>> grouped.apply(lambda x:x.min()) # # 对 bob 和 sos 两组数据使用 min 方法 A B C DA bob bob one 2 1sos sos one 1 2 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106804881未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Pandas","slug":"Python-数据分析/Pandas","permalink":"https://www.itrhx.com/categories/Python-数据分析/Pandas/"}],"tags":[{"name":"Pandas","slug":"Pandas","permalink":"https://www.itrhx.com/tags/Pandas/"},{"name":"GroupBy","slug":"GroupBy","permalink":"https://www.itrhx.com/tags/GroupBy/"},{"name":"数据分裂","slug":"数据分裂","permalink":"https://www.itrhx.com/tags/数据分裂/"},{"name":"数据合并","slug":"数据合并","permalink":"https://www.itrhx.com/tags/数据合并/"}]},{"title":"Python 数据分析三剑客之 Pandas(五):统计计算与统计描述","slug":"A83-Pandas-05","date":"2020-06-16T13:33:14.569Z","updated":"2020-07-06T13:44:54.663Z","comments":true,"path":"2020/06/16/A83-Pandas-05/","link":"","permalink":"https://www.itrhx.com/2020/06/16/A83-Pandas-05/","excerpt":"","text":"Pandas 系列文章: Python 数据分析三剑客之 Pandas(一):认识 Pandas 及其 Series、DataFrame 对象 Python 数据分析三剑客之 Pandas(二):Index 索引对象以及各种索引操作 Python 数据分析三剑客之 Pandas(三):算术运算与缺失值的处理 Python 数据分析三剑客之 Pandas(四):函数应用、映射、排序和层级索引 Python 数据分析三剑客之 Pandas(五):统计计算与统计描述 Python 数据分析三剑客之 Pandas(六):GroupBy 数据分裂、应用与合并 Python 数据分析三剑客之 Pandas(七):合并数据集 Python 数据分析三剑客之 Pandas(八):数据重塑、重复数据处理与数据替换 Python 数据分析三剑客之 Pandas(九):时间序列 Python 数据分析三剑客之 Pandas(十):数据读写 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106788501未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【01x00】统计计算Pandas 对象拥有一组常用的数学和统计方法。它们大部分都属于约简和汇总统计,用于从 Series 中提取单个值(如 sum 或 mean)或从 DataFrame 的行或列中提取一个 Series。跟对应的 NumPy 数组方法相比,它们都是基于没有缺失数据的假设而构建的。 【01x01】sum() 求和sum() 方法用于返回指定轴的和,相当于 numpy.sum()。 在 Series 和 DataFrame 中的基本语法如下: Series.sum(self, axis=None, skipna=None, level=None, numeric_only=None, min_count=0, **kwargs) DataFrame.sum(self, axis=None, skipna=None, level=None, numeric_only=None, min_count=0, **kwargs) 官方文档: https://pandas.pydata.org/docs/reference/api/pandas.Series.sum.html https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.sum.html 常用参数描述如下: 参数 描述 axis 指定轴求和,0 or ‘index’,1 or ‘columns’,只有在 DataFrame 中才有 1 or 'columns’ skipna bool 类型,求和时是否排除缺失值(NA/null),默认 True level 如果轴是 MultiIndex(层次结构),则沿指定层次求和 在 Series 中的应用: 12345678910111213141516171819202122232425262728>>> import pandas as pd>>> idx = pd.MultiIndex.from_arrays([ ['warm', 'warm', 'cold', 'cold'], ['dog', 'falcon', 'fish', 'spider']], names=['blooded', 'animal'])>>> obj = pd.Series([4, 2, 0, 8], name='legs', index=idx)>>> objblooded animalwarm dog 4 falcon 2cold fish 0 spider 8Name: legs, dtype: int64>>> >>> obj.sum()14>>> >>> obj.sum(level='blooded')bloodedwarm 6cold 8Name: legs, dtype: int64>>> >>> obj.sum(level=0)bloodedwarm 6cold 8Name: legs, dtype: int64 在 DataFrame 中的应用: 123456789101112131415161718192021222324>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame([[1.4, np.nan], [7.1, -4.5], [np.nan, np.nan], [0.75, -1.3]], index=['a', 'b', 'c', 'd'], columns=['one', 'two'])>>> obj one twoa 1.40 NaNb 7.10 -4.5c NaN NaNd 0.75 -1.3>>> >>> obj.sum()one 9.25two -5.80dtype: float64>>> >>> obj.sum(axis=1)a 1.40b 2.60c 0.00d -0.55dtype: float64 【01x02】min() 最小值min() 方法用于返回指定轴的最小值。 在 Series 和 DataFrame 中的基本语法如下: Series.min(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs) DataFrame.min(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs) 官方文档: https://pandas.pydata.org/docs/reference/api/pandas.Series.min.html https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.min.html 常用参数描述如下: 参数 描述 axis 指定轴求最小值,0 or ‘index’,1 or ‘columns’,只有在 DataFrame 中才有 1 or 'columns’ skipna bool 类型,求最小值时是否排除缺失值(NA/null),默认 True level 如果轴是 MultiIndex(层次结构),则沿指定层次求最小值 在 Series 中的应用: 12345678910111213141516171819202122232425262728>>> import pandas as pd>>> idx = pd.MultiIndex.from_arrays([ ['warm', 'warm', 'cold', 'cold'], ['dog', 'falcon', 'fish', 'spider']], names=['blooded', 'animal'])>>> obj = pd.Series([4, 2, 0, 8], name='legs', index=idx)>>> objblooded animalwarm dog 4 falcon 2cold fish 0 spider 8Name: legs, dtype: int64>>> >>> obj.min()0>>> >>> obj.min(level='blooded')bloodedwarm 2cold 0Name: legs, dtype: int64>>> >>> obj.min(level=0)bloodedwarm 2cold 0Name: legs, dtype: int64 在 DataFrame 中的应用: 123456789101112131415161718192021222324252627282930>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame([[1.4, np.nan], [7.1, -4.5], [np.nan, np.nan], [0.75, -1.3]], index=['a', 'b', 'c', 'd'],columns=['one', 'two'])>>> obj one twoa 1.40 NaNb 7.10 -4.5c NaN NaNd 0.75 -1.3>>> >>> obj.min()one 0.75two -4.50dtype: float64>>> >>> obj.min(axis=1)a 1.4b -4.5c NaNd -1.3dtype: float64>>> >>> obj.min(axis='columns', skipna=False)a NaNb -4.5c NaNd -1.3dtype: float64 【01x03】max() 最大值max() 方法用于返回指定轴的最大值。 在 Series 和 DataFrame 中的基本语法如下: Series.max(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs) DataFrame.max(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs) 官方文档: https://pandas.pydata.org/docs/reference/api/pandas.Series.max.html https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.max.html 常用参数描述如下: 参数 描述 axis 指定轴求最大值,0 or ‘index’,1 or ‘columns’,只有在 DataFrame 中才有 1 or 'columns’ skipna bool 类型,求最大值时是否排除缺失值(NA/null),默认 True level 如果轴是 MultiIndex(层次结构),则沿指定层次求最大值 在 Series 中的应用: 12345678910111213141516171819202122232425262728>>> import pandas as pd>>> idx = pd.MultiIndex.from_arrays([ ['warm', 'warm', 'cold', 'cold'], ['dog', 'falcon', 'fish', 'spider']], names=['blooded', 'animal'])>>> obj = pd.Series([4, 2, 0, 8], name='legs', index=idx)>>> objblooded animalwarm dog 4 falcon 2cold fish 0 spider 8Name: legs, dtype: int64>>> >>> obj.max()8>>> >>> obj.max(level='blooded')bloodedwarm 4cold 8Name: legs, dtype: int64>>> >>> obj.max(level=0)bloodedwarm 4cold 8Name: legs, dtype: int64 在 DataFrame 中的应用: 123456789101112131415161718192021222324252627282930>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame([[1.4, np.nan], [7.1, -4.5], [np.nan, np.nan], [0.75, -1.3]], index=['a', 'b', 'c', 'd'],columns=['one', 'two'])>>> obj one twoa 1.40 NaNb 7.10 -4.5c NaN NaNd 0.75 -1.3>>> >>> obj.max()one 7.1two -1.3dtype: float64>>> >>> obj.max(axis=1)a 1.40b 7.10c NaNd 0.75dtype: float64>>> >>> obj.max(axis='columns', skipna=False)a NaNb 7.10c NaNd 0.75dtype: float64 【01x04】mean() 平均值mean() 方法用于返回指定轴的平均值。 在 Series 和 DataFrame 中的基本语法如下: Series.mean(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs) DataFrame.mean(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs) 官方文档: https://pandas.pydata.org/docs/reference/api/pandas.Series.mean.html https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.mean.html 常用参数描述如下: 参数 描述 axis 指定轴求平均值,0 or ‘index’,1 or ‘columns’,只有在 DataFrame 中才有 1 or 'columns’ skipna bool 类型,求平均值时是否排除缺失值(NA/null),默认 True level 如果轴是 MultiIndex(层次结构),则沿指定层次求平均值 在 Series 中的应用: 12345678910111213141516171819202122232425262728>>> import pandas as pd>>> idx = pd.MultiIndex.from_arrays([ ['warm', 'warm', 'cold', 'cold'], ['dog', 'falcon', 'fish', 'spider']], names=['blooded', 'animal'])>>> obj = pd.Series([4, 2, 0, 8], name='legs', index=idx)>>> objblooded animalwarm dog 4 falcon 2cold fish 0 spider 8Name: legs, dtype: int64>>> >>> obj.mean()3.5>>> >>> obj.mean(level='blooded')bloodedwarm 3cold 4Name: legs, dtype: int64>>> >>> obj.mean(level=0)bloodedwarm 3cold 4Name: legs, dtype: int64 在 DataFrame 中的应用: 123456789101112131415161718192021222324252627282930>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame([[1.4, np.nan], [7.1, -4.5], [np.nan, np.nan], [0.75, -1.3]], index=['a', 'b', 'c', 'd'],columns=['one', 'two'])>>> obj one twoa 1.40 NaNb 7.10 -4.5c NaN NaNd 0.75 -1.3>>> >>> obj.mean()one 3.083333two -2.900000dtype: float64>>> >>> obj.mean(axis=1)a 1.400b 1.300c NaNd -0.275dtype: float64>>> >>> obj.mean(axis='columns', skipna=False)a NaNb 1.300c NaNd -0.275dtype: float64 【01x05】idxmin() 最小值索引idxmin() 方法用于返回最小值的索引。 在 Series 和 DataFrame 中的基本语法如下: Series.idxmin(self, axis=0, skipna=True, *args, **kwargs) DataFrame.idxmin(self, axis=0, skipna=True) 官方文档: https://pandas.pydata.org/docs/reference/api/pandas.Series.idxmin.html https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.idxmin.html 常用参数描述如下: 参数 描述 axis 指定轴,0 or ‘index’,1 or ‘columns’,只有在 DataFrame 中才有 1 or 'columns’ skipna bool 类型,是否排除缺失值(NA/null),默认 True 在 Series 中的应用: 12345678910111213141516>>> import pandas as pd>>> idx = pd.MultiIndex.from_arrays([ ['warm', 'warm', 'cold', 'cold'], ['dog', 'falcon', 'fish', 'spider']], names=['blooded', 'animal'])>>> obj = pd.Series([4, 2, 0, 8], name='legs', index=idx)>>> objblooded animalwarm dog 4 falcon 2cold fish 0 spider 8Name: legs, dtype: int64>>> >>> obj.idxmin()('cold', 'fish') 在 DataFrame 中的应用: 12345678910111213141516>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame([[1.4, np.nan], [7.1, -4.5], [np.nan, np.nan], [0.75, -1.3]], index=['a', 'b', 'c', 'd'],columns=['one', 'two'])>>> obj one twoa 1.40 NaNb 7.10 -4.5c NaN NaNd 0.75 -1.3>>> >>> obj.idxmin()one dtwo bdtype: object 【01x06】idxmax() 最大值索引idxmax() 方法用于返回最大值的索引。 在 Series 和 DataFrame 中的基本语法如下: Series.idxmax(self, axis=0, skipna=True, *args, **kwargs) DataFrame.idxmax(self, axis=0, skipna=True) 官方文档: https://pandas.pydata.org/docs/reference/api/pandas.Series.idxmax.html https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.idxmax.html 常用参数描述如下: 参数 描述 axis 指定轴,0 or ‘index’,1 or ‘columns’,只有在 DataFrame 中才有 1 or 'columns’ skipna bool 类型,是否排除缺失值(NA/null),默认 True 在 Series 中的应用: 12345678910111213141516>>> import pandas as pd>>> idx = pd.MultiIndex.from_arrays([ ['warm', 'warm', 'cold', 'cold'], ['dog', 'falcon', 'fish', 'spider']], names=['blooded', 'animal'])>>> obj = pd.Series([4, 2, 0, 8], name='legs', index=idx)>>> objblooded animalwarm dog 4 falcon 2cold fish 0 spider 8Name: legs, dtype: int64>>> >>> obj.idxmax()('cold', 'spider') 在 DataFrame 中的应用: 12345678910111213141516>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame([[1.4, np.nan], [7.1, -4.5], [np.nan, np.nan], [0.75, -1.3]], index=['a', 'b', 'c', 'd'],columns=['one', 'two'])>>> obj one twoa 1.40 NaNb 7.10 -4.5c NaN NaNd 0.75 -1.3>>> >>> obj.idxmax()one btwo ddtype: object 【02x00】统计描述describe() 方法用于快速综合统计结果:计数、均值、标准差、最大最小值、四分位数等。还可以通过参数来设置需要忽略或者包含的统计选项。 在 Series 和 DataFrame 中的基本语法如下: Series.describe(self: ~ FrameOrSeries, percentiles=None, include=None, exclude=None) DataFrame.describe(self: ~ FrameOrSeries, percentiles=None, include=None, exclude=None) 官方文档: https://pandas.pydata.org/docs/reference/api/pandas.Series.describe.html https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.describe.html 参数 描述 percentiles 数字列表,可选项,要包含在输出中的百分比。所有值都应介于 0 和 1 之间。默认值为 [.25、.5、.75],即返回第 25、50 和 75 个百分点 include 要包含在结果中的数据类型,数据类型列表,默认 None,具体取值类型参见官方文档 exclude 要从结果中忽略的数据类型,数据类型列表,默认 None,具体取值类型参见官方文档 描述数字形式的 Series 对象: 123456789101112131415161718>>> import pandas as pd>>> obj = pd.Series([1, 2, 3])>>> obj0 11 22 3dtype: int64>>> >>> obj.describe()count 3.0mean 2.0std 1.0min 1.025% 1.550% 2.075% 2.5max 3.0dtype: float64 分类描述: 123456789101112131415>>> import pandas as pd>>> obj = pd.Series(['a', 'a', 'b', 'c'])>>> obj0 a1 a2 b3 cdtype: object>>> >>> obj.describe()count 4unique 3top afreq 2dtype: object 描述时间戳: 1234567891011121314151617181920>>> import pandas as pd>>> obj = pd.Series([ np.datetime64(\"2000-01-01\"), np.datetime64(\"2010-01-01\"), np.datetime64(\"2010-01-01\") ])>>> obj0 2000-01-011 2010-01-012 2010-01-01dtype: datetime64[ns]>>> >>> obj.describe()count 3unique 2top 2010-01-01 00:00:00freq 2first 2000-01-01 00:00:00last 2010-01-01 00:00:00dtype: object 描述 DataFrame 对象: 123456789101112131415161718>>> import pandas as pd>>> obj = pd.DataFrame({'categorical': pd.Categorical(['d','e','f']), 'numeric': [1, 2, 3], 'object': ['a', 'b', 'c']})>>> obj categorical numeric object0 d 1 a1 e 2 b2 f 3 c>>> >>> obj.describe() numericcount 3.0mean 2.0std 1.0min 1.025% 1.550% 2.075% 2.5max 3.0 不考虑数据类型,显示所有描述: 123456789101112131415161718192021>>> import pandas as pd>>> obj = pd.DataFrame({'categorical': pd.Categorical(['d','e','f']), 'numeric': [1, 2, 3], 'object': ['a', 'b', 'c']})>>> obj categorical numeric object0 d 1 a1 e 2 b2 f 3 c>>> >>> obj.describe(include='all') categorical numeric objectcount 3 3.0 3unique 3 NaN 3top f NaN cfreq 1 NaN 1mean NaN 2.0 NaNstd NaN 1.0 NaNmin NaN 1.0 NaN25% NaN 1.5 NaN50% NaN 2.0 NaN75% NaN 2.5 NaNmax NaN 3.0 NaN 仅包含 category 列: 1234567891011121314>>> import pandas as pd>>> obj = pd.DataFrame({'categorical': pd.Categorical(['d','e','f']), 'numeric': [1, 2, 3], 'object': ['a', 'b', 'c']})>>> obj categorical numeric object0 d 1 a1 e 2 b2 f 3 c>>> >>> obj.describe(include=['category']) categoricalcount 3unique 3top ffreq 1 【03x00】常用统计方法其他常用统计方法参见下表: 方法 描述 官方文档 count 非NA值的数量 Series丨DataFrame describe 针对Series或各DataFrame列计算汇总统计 Series丨DataFrame min 计算最小值 Series丨DataFrame max 计算最大值 Series丨DataFrame argmin 计算能够获取到最小值的索引位置(整数) Series argmax 计算能够获取到最大值的索引位置(整数) Series idxmin 计算能够获取到最小值的索引值 Series丨DataFrame idxmax 计算能够获取到最大值的索引值 Series丨DataFrame quantile 计算样本的分位数(0到1) Series丨DataFrame sum 值的总和 Series丨DataFrame mean 值的平均数 Series丨DataFrame median 值的算术中位数(50%分位数) Series丨DataFrame mad 根据平均值计算平均绝对离差 Series丨DataFrame var 样本值的方差 Series丨DataFrame std 样本值的标准差 Series丨DataFrame 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106788501未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Pandas","slug":"Python-数据分析/Pandas","permalink":"https://www.itrhx.com/categories/Python-数据分析/Pandas/"}],"tags":[{"name":"Pandas","slug":"Pandas","permalink":"https://www.itrhx.com/tags/Pandas/"},{"name":"统计计算","slug":"统计计算","permalink":"https://www.itrhx.com/tags/统计计算/"},{"name":"统计描述","slug":"统计描述","permalink":"https://www.itrhx.com/tags/统计描述/"}]},{"title":"Python 数据分析三剑客之 Pandas(四):函数应用/映射/排序和层级索引","slug":"A82-Pandas-04","date":"2020-06-15T12:42:36.626Z","updated":"2020-07-06T13:44:46.745Z","comments":true,"path":"2020/06/15/A82-Pandas-04/","link":"","permalink":"https://www.itrhx.com/2020/06/15/A82-Pandas-04/","excerpt":"","text":"Pandas 系列文章: Python 数据分析三剑客之 Pandas(一):认识 Pandas 及其 Series、DataFrame 对象 Python 数据分析三剑客之 Pandas(二):Index 索引对象以及各种索引操作 Python 数据分析三剑客之 Pandas(三):算术运算与缺失值的处理 Python 数据分析三剑客之 Pandas(四):函数应用、映射、排序和层级索引 Python 数据分析三剑客之 Pandas(五):统计计算与统计描述 Python 数据分析三剑客之 Pandas(六):GroupBy 数据分裂、应用与合并 Python 数据分析三剑客之 Pandas(七):合并数据集 Python 数据分析三剑客之 Pandas(八):数据重塑、重复数据处理与数据替换 Python 数据分析三剑客之 Pandas(九):时间序列 Python 数据分析三剑客之 Pandas(十):数据读写 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106758103未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【01x00】函数应用和映射Pandas 可直接使用 NumPy 的 ufunc(元素级数组方法) 函数: 123456789101112131415161718>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame(np.random.randn(5,4) - 1)>>> obj 0 1 2 30 -0.228107 1.377709 -1.096528 -2.0510011 -2.477144 -0.500013 -0.040695 -0.2674522 -0.485999 -1.232930 -0.390701 -1.9479843 -0.839161 -0.702802 -1.756359 -1.8731494 0.853121 -1.540105 0.621614 -0.583360>>> >>> np.abs(obj) 0 1 2 30 0.228107 1.377709 1.096528 2.0510011 2.477144 0.500013 0.040695 0.2674522 0.485999 1.232930 0.390701 1.9479843 0.839161 0.702802 1.756359 1.8731494 0.853121 1.540105 0.621614 0.583360 函数映射:在 Pandas 中 apply 方法可以将函数应用到列或行上,可以通过设置 axis 参数来指定行或列,默认 axis = 0,即按列映射: 12345678910111213141516171819202122232425>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame(np.random.randn(5,4) - 1)>>> obj 0 1 2 30 -0.707028 -0.755552 -2.196480 -0.5296761 -0.772668 0.127485 -2.015699 -0.2836542 0.248200 -1.940189 -1.068028 -1.7517373 -0.872904 -0.465371 -1.327951 -2.8831604 -0.092664 0.258351 -1.010747 -2.313039>>> >>> obj.apply(lambda x : x.max())0 0.2482001 0.2583512 -1.0107473 -0.283654dtype: float64>>>>>> obj.apply(lambda x : x.max(), axis=1)0 -0.5296761 0.1274852 0.2482003 -0.4653714 0.258351dtype: float64 另外还可以通过 applymap 将函数映射到每个数据上: 123456789101112131415161718>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame(np.random.randn(5,4) - 1)>>> obj 0 1 2 30 -0.772463 -1.597008 -3.196100 -1.9484861 -1.765108 -1.646421 -0.687175 -0.4017822 0.275699 -3.115184 -1.429063 -1.0756103 -0.251734 -0.448399 -3.077677 -0.2946744 -1.495896 -1.689729 -0.560376 -1.808794>>> >>> obj.applymap(lambda x : '%.2f' % x) 0 1 2 30 -0.77 -1.60 -3.20 -1.951 -1.77 -1.65 -0.69 -0.402 0.28 -3.12 -1.43 -1.083 -0.25 -0.45 -3.08 -0.294 -1.50 -1.69 -0.56 -1.81 【02x00】排序【02x01】sort_index() 索引排序根据条件对数据集排序(sorting)也是一种重要的内置运算。要对行或列索引进行排序(按字典顺序),可使用 sort_index 方法,它将返回一个已排序的新对象。 在 Series 和 DataFrame 中的基本语法如下: 123456789Series.sort_index(self, axis=0, level=None, ascending=True, inplace=False, kind='quicksort', na_position='last', sort_remaining=True, ignore_index: bool = False) 123456789DataFrame.sort_index(self, axis=0, level=None, ascending=True, inplace=False, kind='quicksort', na_position='last', sort_remaining=True, ignore_index: bool = False) 官方文档: https://pandas.pydata.org/docs/reference/api/pandas.Series.sort_index.html https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.sort_index.html 常用参数描述如下: 参数 描述 axis 指定轴排序,0 or ‘index’,1 or ‘columns’,只有在 DataFrame 中才有 1 or 'columns’ ascending 为 True时升序排序(默认),为 False时降序排序 kind 排序方法,quicksort:快速排序(默认);'mergesort’:归并排序;'heapsort':堆排序;具体可参见 numpy.sort() 在 Series 中的应用(按照索引 index 排序): 123456789101112131415>>> import pandas as pd>>> obj = pd.Series(range(4), index=['d', 'a', 'b', 'c'])>>> objd 0a 1b 2c 3dtype: int64>>> >>> obj.sort_index()a 1b 2c 3d 0dtype: int64 在 DataFrame 中的应用(可按照索引 index 或列标签 columns 排序): 123456789101112131415161718192021>>> import pandas as pd>>> obj = pd.DataFrame(np.arange(8).reshape((2, 4)), index=['three', 'one'], columns=['d', 'a', 'b', 'c'])>>> obj d a b cthree 0 1 2 3one 4 5 6 7>>> >>> obj.sort_index() d a b cone 4 5 6 7three 0 1 2 3>>> >>> obj.sort_index(axis=1) a b c dthree 1 2 3 0one 5 6 7 4>>> >>> obj.sort_index(axis=1, ascending=False) d c b athree 0 3 2 1one 4 7 6 5 【02x02】sort_values() 按值排序在 Series 和 DataFrame 中的基本语法如下: 1234567Series.sort_values(self, axis=0, ascending=True, inplace=False, kind='quicksort', na_position='last', ignore_index=False) 12345678DataFrame.sort_values(self, by, axis=0, ascending=True, inplace=False, kind='quicksort', na_position='last', ignore_index=False) 官方文档: https://pandas.pydata.org/docs/reference/api/pandas.Series.sort_values.html https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.sort_values.html 常用参数描述如下: 参数 描述 by DataFrame 中的必须参数,指定列的值进行排序,Series 中没有此参数 axis 指定轴排序,0 or ‘index’,1 or ‘columns’,只有在 DataFrame 中才有 1 or 'columns’ ascending 为 True时升序排序(默认),为 False时降序排序 kind 排序方法,quicksort:快速排序(默认);'mergesort’:归并排序;'heapsort':堆排序;具体可参见 numpy.sort() 在 Series 中的应用,按照值排序,如果有缺失值,默认都会被放到 Series 的末尾: 12345678910111213141516171819202122232425262728293031323334>>> import pandas as pd>>> obj = pd.Series([4, 7, -3, 2])>>> obj0 41 72 -33 2dtype: int64>>> >>> obj.sort_values()2 -33 20 41 7dtype: int64>>> >>> obj = pd.Series([4, np.nan, 7, np.nan, -3, 2])>>> obj0 4.01 NaN2 7.03 NaN4 -3.05 2.0dtype: float64>>> >>> obj.sort_values()4 -3.05 2.00 4.02 7.01 NaN3 NaNdtype: float64 在 DataFrame 中的应用,有时候可能希望根据一个或多个列中的值进行排序。将一个或多个列的名字传递给 sort_values() 的 by 参数即可达到该目的,当传递多个列时,首先会对第一列进行排序,若第一列有相同的值,再根据第二列进行排序,依次类推: 1234567891011121314151617181920212223242526272829>>> import pandas as pd>>> obj = pd.DataFrame({'a': [4, 4, -3, 2], 'b': [0, 1, 0, 1], 'c': [6, 4, 1, 3]})>>> obj a b c0 4 0 61 4 1 42 -3 0 13 2 1 3>>> >>> obj.sort_values(by='c') a b c2 -3 0 13 2 1 31 4 1 40 4 0 6>>> >>> obj.sort_values(by='c', ascending=False) a b c0 4 0 61 4 1 43 2 1 32 -3 0 1>>>>>> obj.sort_values(by=['a', 'b']) a b c2 -3 0 13 2 1 30 4 0 61 4 1 4 123456789101112131415>>> import pandas as pd>>> obj = pd.DataFrame({'a': [4, 4, -3, 2], 'b': [0, 1, 0, 1], 'c': [6, 4, 1, 3]}, index=['A', 'B', 'C', 'D'])>>> obj a b cA 4 0 6B 4 1 4C -3 0 1D 2 1 3>>> >>> obj.sort_values(by='B', axis=1) b a cA 0 4 6B 1 4 4C 0 -3 1D 1 2 3 【02x03】rank() 返回排序后元素索引rank() 函数会返回一个对象,对象的值是原对象经过排序后的索引值,即下标。 在 Series 和 DataFrame 中的基本语法如下: 1234567Series.rank(self: ~ FrameOrSeries, axis=0, method: str = 'average', numeric_only: Union[bool, NoneType] = None, na_option: str = 'keep', ascending: bool = True, pct: bool = False) 1234567DataFrame.rank(self: ~ FrameOrSeries, axis=0, method: str = 'average', numeric_only: Union[bool, NoneType] = None, na_option: str = 'keep', ascending: bool = True, pct: bool = False) 官方文档: https://pandas.pydata.org/docs/reference/api/pandas.Series.rank.html https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.rank.html 常用参数描述如下: 参数 描述 axis 指定轴排序,0 or ‘index’,1 or ‘columns’,只有在 DataFrame 中才有 1 or 'columns’ method 有相同值时,如何处理:‘average’:默认值,去两个相同索引的平均值;‘min’:取两个相同索引的最小值;‘max’:取两个相同索引的最大值;‘first’:按照出现的先后顺序;‘dense’:和 'min' 差不多,但是各组之间总是+1的,不太好解释,可以看后面的示例 ascending 为 True时升序排序(默认),为 False时降序排序 在 Series 中的应用,按照值排序,如果有缺失值,默认都会被放到 Series 的末尾: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051>>> import pandas as pd>>> obj = pd.Series([7, -5, 7, 4, 2, 0, 4])>>> obj0 71 -52 73 44 25 06 4dtype: int64>>> >>> obj.rank()0 6.5 # 第 0 个和第 2 个值从小到大排名分别为 6 和 7,默认取平均值,即 6.51 1.02 6.53 4.5 # 第 3 个和第 6 个值从小到大排名分别为 4 和 5,默认取平均值,即 4.54 3.05 2.06 4.5dtype: float64>>> >>> obj.rank(method='first')0 6.0 # 第 0 个和第 2 个值从小到大排名分别为 6 和 7,按照第一次出现排序,分别为 6 和 71 1.02 7.03 4.0 # 第 3 个和第 6 个值从小到大排名分别为 4 和 5,按照第一次出现排序,分别为 4 和 54 3.05 2.06 5.0dtype: float64>>> >>> obj.rank(method='dense')0 5.0 # 第 0 个和第 2 个值从小到大排名分别为 6 和 7,按照最小值排序,但 dense 规定间隔为 1 所以为 51 1.02 5.03 4.0 # 第 3 个和第 6 个值从小到大排名分别为 4 和 5,按照最小值排序,即 44 3.05 2.06 4.0dtype: float64>>> >>> obj.rank(method='min')0 6.0 # 第 0 个和第 2 个值从小到大排名分别为 6 和 7,按照最小值排序,即 61 1.02 6.03 4.0 # 第 3 个和第 6 个值从小到大排名分别为 4 和 5,按照最小值排序,即 44 3.05 2.06 4.0dtype: float64 在 DataFrame 中可以使用 axis 参数来指定轴: 12345678910111213141516171819202122>>> import pandas as pd>>> obj = pd.DataFrame({'b': [4.3, 7, -3, 2], 'a': [0, 1, 0, 1], 'c': [-2, 5, 8, -2.5]})>>> obj b a c0 4.3 0 -2.01 7.0 1 5.02 -3.0 0 8.03 2.0 1 -2.5>>> >>> obj.rank() b a c0 3.0 1.5 2.01 4.0 3.5 3.02 1.0 1.5 4.03 2.0 3.5 1.0>>> >>> obj.rank(axis='columns') b a c0 3.0 2.0 1.01 3.0 1.0 2.02 1.0 2.0 3.03 3.0 2.0 1.0 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106758103未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【03x00】层级索引【03x01】认识层级索引以下示例将创建一个 Series 对象, 索引 Index 由两个子 list 组成,第一个子 list 是外层索引,第二个 list 是内层索引: 1234567891011121314151617>>> import pandas as pd>>> import numpy as np>>> obj = pd.Series(np.random.randn(12),index=[['a', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'c', 'd', 'd', 'd'], [0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2]])>>> obja 0 -0.201536 1 -0.629058 2 0.766716b 0 -1.255831 1 -0.483727 2 -0.018653c 0 0.788787 1 1.010097 2 -0.187258d 0 1.242363 1 -0.822011 2 -0.085682dtype: float64 【03x02】MultiIndex 索引对象官方文档:https://pandas.pydata.org/docs/reference/api/pandas.MultiIndex.html 尝试打印上面示例中 Series 的索引类型,会得到一个 MultiIndex 对象,MultiIndex 对象的 levels 属性表示两个层级中分别有那些标签,codes 属性表示每个位置分别是什么标签,如下所示: 12345678910111213141516171819202122232425262728293031323334353637383940>>> import pandas as pd>>> import numpy as np>>> obj = pd.Series(np.random.randn(12),index=[['a', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'c', 'd', 'd', 'd'], [0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2]])>>> obja 0 0.035946 1 -0.867215 2 -0.053355b 0 -0.986616 1 0.026071 2 -0.048394c 0 0.251274 1 0.217790 2 1.137674d 0 -1.245178 1 1.234972 2 -0.035624dtype: float64>>> >>> type(obj.index)<class 'pandas.core.indexes.multi.MultiIndex'>>>> >>> obj.indexMultiIndex([('a', 0), ('a', 1), ('a', 2), ('b', 0), ('b', 1), ('b', 2), ('c', 0), ('c', 1), ('c', 2), ('d', 0), ('d', 1), ('d', 2)], )>>> obj.index.levelsFrozenList([['a', 'b', 'c', 'd'], [0, 1, 2]])>>>>>> obj.index.codesFrozenList([[0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3], [0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2]]) 通常可以使用 from_arrays() 方法来将数组对象转换为 MultiIndex 索引对象: 1234567>>> arrays = [[1, 1, 2, 2], ['red', 'blue', 'red', 'blue']]>>> pd.MultiIndex.from_arrays(arrays, names=('number', 'color'))MultiIndex([(1, 'red'), (1, 'blue'), (2, 'red'), (2, 'blue')], names=['number', 'color']) 其他常用方法见下表(更多方法参见官方文档): 方法 描述 from_arrays(arrays[, sortorder, names]) 将数组转换为 MultiIndex from_tuples(tuples[, sortorder, names]) 将元组列表转换为 MultiIndex from_product(iterables[, sortorder, names]) 将多个可迭代的笛卡尔积转换成 MultiIndex from_frame(df[, sortorder, names]) 将 DataFrame 对象转换为 MultiIndex set_levels(self, levels[, level, inplace, …]) 为 MultiIndex 设置新的 levels set_codes(self, codes[, level, inplace, …]) 为 MultiIndex 设置新的 codes sortlevel(self[, level, ascending, …]) 根据 level 进行排序 droplevel(self[, level]) 删除指定的 level swaplevel(self[, i, j]) 交换 level i 与 level i,即交换外层索引与内层索引 【03x03】提取值对于这种有多层索引的对象,如果只传入一个参数,则会对外层索引进行提取,其中包含对应所有的内层索引,如果传入两个参数,则第一个参数表示外层索引,第二个参数表示内层索引,示例如下: 123456789101112131415161718192021222324252627282930313233>>> import pandas as pd>>> import numpy as np>>> obj = pd.Series(np.random.randn(12),index=[['a', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'c', 'd', 'd', 'd'], [0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2]])>>> obja 0 0.550202 1 0.328784 2 1.422690b 0 -1.333477 1 -0.933809 2 -0.326541c 0 0.663686 1 0.943393 2 0.273106d 0 1.354037 1 -2.312847 2 -2.343777dtype: float64>>> >>> obj['b']0 -1.3334771 -0.9338092 -0.326541dtype: float64>>>>>> obj['b', 1]-0.9338094811708413>>> >>> obj[:, 2]a 1.422690b -0.326541c 0.273106d -2.343777dtype: float64 【03x04】交换分层与排序MultiIndex 对象的 swaplevel() 方法可以交换外层与内层索引,sortlevel() 方法会先对外层索引进行排序,再对内层索引进行排序,默认是升序,如果设置 ascending 参数为 False 则会降序排列,示例如下: 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647>>> import pandas as pd>>> import numpy as np>>> obj = pd.Series(np.random.randn(12),index=[['a', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'c', 'd', 'd', 'd'], [0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2]])>>> obja 0 -0.110215 1 0.193075 2 -1.101706b 0 -1.325743 1 0.528418 2 -0.127081c 0 -0.733822 1 1.665262 2 0.127073d 0 1.262022 1 -1.170518 2 0.966334dtype: float64>>> >>> obj.swaplevel()0 a -0.1102151 a 0.1930752 a -1.1017060 b -1.3257431 b 0.5284182 b -0.1270810 c -0.7338221 c 1.6652622 c 0.1270730 d 1.2620221 d -1.1705182 d 0.966334dtype: float64>>> >>> obj.swaplevel().index.sortlevel()(MultiIndex([(0, 'a'), (0, 'b'), (0, 'c'), (0, 'd'), (1, 'a'), (1, 'b'), (1, 'c'), (1, 'd'), (2, 'a'), (2, 'b'), (2, 'c'), (2, 'd')], ), array([ 0, 3, 6, 9, 1, 4, 7, 10, 2, 5, 8, 11], dtype=int32)) 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106758103未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Pandas","slug":"Python-数据分析/Pandas","permalink":"https://www.itrhx.com/categories/Python-数据分析/Pandas/"}],"tags":[{"name":"Pandas","slug":"Pandas","permalink":"https://www.itrhx.com/tags/Pandas/"},{"name":"函数应用","slug":"函数应用","permalink":"https://www.itrhx.com/tags/函数应用/"},{"name":"映射","slug":"映射","permalink":"https://www.itrhx.com/tags/映射/"},{"name":"排序","slug":"排序","permalink":"https://www.itrhx.com/tags/排序/"},{"name":"层级索引","slug":"层级索引","permalink":"https://www.itrhx.com/tags/层级索引/"}]},{"title":"Python 数据分析三剑客之 Pandas(三):算术运算与缺失值的处理","slug":"A81-Pandas-03","date":"2020-06-14T15:36:58.366Z","updated":"2020-07-06T13:44:36.650Z","comments":true,"path":"2020/06/14/A81-Pandas-03/","link":"","permalink":"https://www.itrhx.com/2020/06/14/A81-Pandas-03/","excerpt":"","text":"Pandas 系列文章: Python 数据分析三剑客之 Pandas(一):认识 Pandas 及其 Series、DataFrame 对象 Python 数据分析三剑客之 Pandas(二):Index 索引对象以及各种索引操作 Python 数据分析三剑客之 Pandas(三):算术运算与缺失值的处理 Python 数据分析三剑客之 Pandas(四):函数应用、映射、排序和层级索引 Python 数据分析三剑客之 Pandas(五):统计计算与统计描述 Python 数据分析三剑客之 Pandas(六):GroupBy 数据分裂、应用与合并 Python 数据分析三剑客之 Pandas(七):合并数据集 Python 数据分析三剑客之 Pandas(八):数据重塑、重复数据处理与数据替换 Python 数据分析三剑客之 Pandas(九):时间序列 Python 数据分析三剑客之 Pandas(十):数据读写 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106743778未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【01x00】Pandas 算术运算Pandas 继承了 NumPy 的功能,NumPy 的基本能力之一是快速对每个元素进行运算,既包括基本算术运算(加、减、乘、除),也包括更复杂的运算(三角函数、指数函数和对数函数等)。具体可以参考 NumPy 系列文章。 【01x01】使用 NumPy 通用函数因为 Pandas 是建立在 NumPy 基础之上的,所以 NumPy 的通用函数同样适用于 Pandas 的 Series 和 DataFrame 对象,如下所示: 1234567891011121314151617>>> import pandas as pd>>> import numpy as np>>> rng = np.random.RandomState(42)>>> ser = pd.Series(rng.randint(0, 10, 4))>>> ser0 61 32 73 4dtype: int32>>> >>> obj = pd.DataFrame(rng.randint(0, 10, (3, 4)), columns=['A', 'B', 'C', 'D'])>>> obj A B C D0 6 9 2 61 7 4 3 72 7 2 5 4 使用 NumPy 通用函数,生成的结果是另一个保留索引的 Pandas 对象: 1234567891011121314151617>>> import pandas as pd>>> import numpy as np>>> rng = np.random.RandomState(42)>>> ser = pd.Series(rng.randint(0, 10, 4))>>> ser0 61 32 73 4dtype: int32>>> >>> np.exp(ser)0 403.4287931 20.0855372 1096.6331583 54.598150dtype: float64 12345678>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame(rng.randint(0, 10, (3, 4)), columns=['A', 'B', 'C', 'D'])>>> np.sin(obj * np.pi / 4) A B C D0 -1.000000 7.071068e-01 1.000000 -1.000000e+001 -0.707107 1.224647e-16 0.707107 -7.071068e-012 -0.707107 1.000000e+00 -0.707107 1.224647e-16 【01x02】数据对齐Pandas 最重要的一个功能是,它可以对不同索引的对象进行算术运算。在将对象相加时,如果存在不同的索引对,则结果的索引就是该索引对的并集。自动的数据对齐操作会在不重叠的索引处引入缺失值,即 NaN,缺失值会在算术运算过程中传播。 Series 对象的数据对齐操作: 1234567891011121314151617181920212223242526>>> import pandas as pd>>> obj1 = pd.Series([7.3, -2.5, 3.4, 1.5], index=['a', 'c', 'd', 'e'])>>> obj2 = pd.Series([-2.1, 3.6, -1.5, 4, 3.1], index=['a', 'c', 'e', 'f', 'g'])>>> obj1a 7.3c -2.5d 3.4e 1.5dtype: float64>>> >>> obj2a -2.1c 3.6e -1.5f 4.0g 3.1dtype: float64>>>>>> obj1 + obj2a 5.2c 1.1d NaNe 0.0f NaNg NaNdtype: float64 DataFrame 对象的数据对齐操作会同时发生在行和列上: 1234567891011121314151617181920212223>>> import pandas as pd>>> obj1 = pd.DataFrame(np.arange(9.).reshape((3, 3)), columns=list('bcd'), index=['Ohio', 'Texas', 'Colorado'])>>> obj2 = pd.DataFrame(np.arange(12.).reshape((4, 3)), columns=list('bde'), index=['Utah', 'Ohio', 'Texas', 'Oregon'])>>> obj1 b c dOhio 0.0 1.0 2.0Texas 3.0 4.0 5.0Colorado 6.0 7.0 8.0>>> >>> obj2 b d eUtah 0.0 1.0 2.0Ohio 3.0 4.0 5.0Texas 6.0 7.0 8.0Oregon 9.0 10.0 11.0>>> >>> obj1 + obj2 b c d eColorado NaN NaN NaN NaNOhio 3.0 NaN 6.0 NaNOregon NaN NaN NaN NaNTexas 9.0 NaN 12.0 NaNUtah NaN NaN NaN NaN 【01x03】DataFrame 与 Series 之间的运算首先回忆 NumPy 中的广播(参见:《Python 数据分析三剑客之 NumPy(二):数组索引 / 切片 / 广播 / 拼接 / 分割》),跟不同维度的 NumPy 数组一样,DataFrame 和 Series 之间算术运算也是有明确规定的。首先回忆一下 NumPy 中不同维度的数组之间的运算: 1234567891011121314>>> import numpy as np>>> arr = np.arange(12.).reshape((3, 4))>>> arrarray([[ 0., 1., 2., 3.], [ 4., 5., 6., 7.], [ 8., 9., 10., 11.]])>>> >>> arr[0]array([0., 1., 2., 3.])>>> >>> arr - arr[0]array([[0., 0., 0., 0.], [4., 4., 4., 4.], [8., 8., 8., 8.]]) 可以看到每一行都进行了减法运算,这正是 NumPy 中的广播,而 DataFrame 与 Series 之间的运算也类似,默认情况下,DataFrame 和 Series 之间的算术运算会将 Series 的索引匹配到 DataFrame 的列,然后沿着行一直向下广播: 1234567891011121314151617181920212223>>> import numpy as np>>> import pandas as pd>>> frame = pd.DataFrame(np.arange(12.).reshape((4, 3)), columns=list('bde'), index=['AA', 'BB', 'CC', 'DD'])>>> frame b d eAA 0.0 1.0 2.0BB 3.0 4.0 5.0CC 6.0 7.0 8.0DD 9.0 10.0 11.0>>> >>> series = frame.iloc[0]>>> seriesb 0.0d 1.0e 2.0Name: AA, dtype: float64>>> >>> frame - series b d eAA 0.0 0.0 0.0BB 3.0 3.0 3.0CC 6.0 6.0 6.0DD 9.0 9.0 9.0 如果某个索引值在 DataFrame 的列或 Series 的索引中找不到,则参与运算的两个对象就会被重新索引以形成并集: 1234567891011121314151617181920212223>>> import numpy as np>>> import pandas as pd>>> frame = pd.DataFrame(np.arange(12.).reshape((4, 3)), columns=list('bde'), index=['AA', 'BB', 'CC', 'DD'])>>> frame b d eAA 0.0 1.0 2.0BB 3.0 4.0 5.0CC 6.0 7.0 8.0DD 9.0 10.0 11.0>>> >>> series = pd.Series(range(3), index=['b', 'e', 'f'])>>> seriesb 0e 1f 2dtype: int64>>> >>> frame + series b d e fAA 0.0 NaN 3.0 NaNBB 3.0 NaN 6.0 NaNCC 6.0 NaN 9.0 NaNDD 9.0 NaN 12.0 NaN 如果希望匹配行且在列上广播,则必须使用算术运算方法,在方法中传入的轴(axis)就是希望匹配的轴。在下例中,我们的目的是匹配 DataFrame 的行索引(axis=’index’ or axis=0)并进行广播: 123456789101112131415161718192021222324>>> import numpy as np>>> import pandas as pd>>> frame = pd.DataFrame(np.arange(12.).reshape((4, 3)), columns=list('bde'), index=['AA', 'BB', 'CC', 'DD'])>>> frame b d eAA 0.0 1.0 2.0BB 3.0 4.0 5.0CC 6.0 7.0 8.0DD 9.0 10.0 11.0>>> >>> series = frame['d']>>> seriesAA 1.0BB 4.0CC 7.0DD 10.0Name: d, dtype: float64>>> >>> frame.sub(series, axis='index') b d eAA -1.0 0.0 1.0BB -1.0 0.0 1.0CC -1.0 0.0 1.0DD -1.0 0.0 1.0 【01x04】Pandas 算术方法完整的 Pandas 算术方法见下表: 方法 副本 描述 add() radd() 加法(+) sub()、subtract() rsub() 减法(-) mul()、multiply() rmul() 乘法(*) pow() rpow() 指数(**) truediv()、div()、divide() rdiv() 除法(/) floordiv() rfloordiv() 底除(//) mod() rmod() 求余(%) 副本均为原方法前加了个 r,它会翻转参数: 12345678910111213141516171819>>> import pandas as pd>>> obj = pd.DataFrame(np.arange(12.).reshape((3, 4)), columns=list('abcd'))>>> obj a b c d0 0.0 1.0 2.0 3.01 4.0 5.0 6.0 7.02 8.0 9.0 10.0 11.0>>> >>> 1 / obj a b c d0 inf 1.000000 0.500000 0.3333331 0.250 0.200000 0.166667 0.1428572 0.125 0.111111 0.100000 0.090909>>> >>> obj.rdiv(1) a b c d0 inf 1.000000 0.500000 0.3333331 0.250 0.200000 0.166667 0.1428572 0.125 0.111111 0.100000 0.090909 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106743778未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【02x00】处理缺失值在现实中遇到的数据很少是干净整齐的,许多数据集都会有数据缺失的现象,缺失值主要有三种形式:null、NaN(NAN,nan) 或 NA。 【02x01】fill_value() 指定值与缺失值进行运算使用 add, sub, div, mul 等算术方法时,通过 fill_value 指定填充值,未对齐的数据将和填充值做运算。 Series 中的应用: 1234567891011121314151617181920212223242526272829303132>>> import pandas as pd>>> obj1 = pd.Series([1, 2, 3, 4, 5])>>> obj2 = pd.Series([6, 7])>>> >>> obj10 11 22 33 44 5dtype: int64>>> >>> obj20 61 7dtype: int64>>> >>> obj1.add(obj2)0 7.01 9.02 NaN3 NaN4 NaNdtype: float64>>> >>> obj1.add(obj2, fill_value=-1)0 7.01 9.02 2.03 3.04 4.0dtype: float64 DataFrame 中的应用: 123456789101112131415161718192021222324252627282930313233>>> import pandas as pd>>> import numpy as np>>> obj1 = pd.DataFrame(np.arange(12.).reshape((3, 4)), columns=list('abcd'))>>> obj2 = pd.DataFrame(np.arange(20.).reshape((4, 5)), columns=list('abcde'))>>> >>> obj2.loc[1, 'b'] = np.nan>>> >>> obj1 a b c d0 0.0 1.0 2.0 3.01 4.0 5.0 6.0 7.02 8.0 9.0 10.0 11.0>>> >>> obj2 a b c d e0 0.0 1.0 2.0 3.0 4.01 5.0 NaN 7.0 8.0 9.02 10.0 11.0 12.0 13.0 14.03 15.0 16.0 17.0 18.0 19.0>>> >>> obj1 + obj2 a b c d e0 0.0 2.0 4.0 6.0 NaN1 9.0 NaN 13.0 15.0 NaN2 18.0 20.0 22.0 24.0 NaN3 NaN NaN NaN NaN NaN>>> >>> obj1.add(obj2, fill_value=10) a b c d e0 0.0 2.0 4.0 6.0 14.01 9.0 15.0 13.0 15.0 19.02 18.0 20.0 22.0 24.0 24.03 25.0 26.0 27.0 28.0 29.0 【02x02】isnull() / notnull() 判断缺失值isnull():为缺失值时为 True,否则为 False; notnull() 为缺失值时为 False,否则为 True。 1234567891011121314151617181920212223>>> import numpy as np>>> import pandas as pd>>> obj = pd.Series([1, np.nan, 'hello', None])>>> obj0 11 NaN2 hello3 Nonedtype: object>>> >>> obj.isnull()0 False1 True2 False3 Truedtype: bool>>> >>> obj.notnull()0 True1 False2 True3 Falsedtype: bool 【02x03】dropna() 删除缺失值dropna() 方法用于返回一个删除了缺失值的新 Series 或 DataFrame 对象。 在 Series 对象当中,dropna() 方法的语法如下(其他参数用法可参考在 DataFrame 中的应用): Series.dropna(self, axis=0, inplace=False, how=None) 官方文档:https://pandas.pydata.org/docs/reference/api/pandas.Series.dropna.html 1234567891011121314>>> import numpy as np>>> import pandas as pd>>> obj = pd.Series([1, np.nan, 'hello', None])>>> obj0 11 NaN2 hello3 Nonedtype: object>>> >>> obj.dropna()0 12 hellodtype: object 在 DataFrame 对象中,dropna() 方法的语法如下: DataFrame.dropna(self, axis=0, how='any', thresh=None, subset=None, inplace=False) 官方文档:https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.dropna.html 参数 描述 axis 确定是否删除包含缺失值的行或列0 或 'index':删除包含缺失值的行。1 或 'columns':删除包含缺失值的列 how 'any':如果存在任何NA值,则删除该行或列。'all':如果所有值都是NA,则删除该行或列 thresh 设置行或列中非缺失值的最小数量 不传递任何参数,将会删除任何包含缺失值的整行数据: 123456789101112>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame([[1, np.nan, 2], [2, 3, 5], [np.nan, 4, 6]])>>> obj 0 1 20 1.0 NaN 21 2.0 3.0 52 NaN 4.0 6>>> >>> obj.dropna() 0 1 21 2.0 3.0 5 指定 axis 参数,删除包含缺失值的行或列: 1234567891011121314>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame([[1, np.nan, 2], [2, 3, 5], [np.nan, 4, 6]])>>> obj 0 1 20 1.0 NaN 21 2.0 3.0 52 NaN 4.0 6>>> >>> obj.dropna(axis='columns') 20 21 52 6 指定 how 参数,'any':如果存在任何NA值,则删除该行或列。'all':如果所有值都是NA,则删除该行或列: 12345678910111213>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame([[1, np.nan, 2, np.nan], [2, 3, 5, np.nan], [np.nan, 4, 6, np.nan]])>>> obj 0 1 2 30 1.0 NaN 2 NaN1 2.0 3.0 5 NaN2 NaN 4.0 6 NaN>>> obj.dropna(axis='columns', how='all') 0 1 20 1.0 NaN 21 2.0 3.0 52 NaN 4.0 6 指定 thresh 参数,设置行或列中非缺失值的最小数量,以下示例中,第一行和第三行只有两个非缺失值,所以会被删除: 123456789101112>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame([[1, np.nan, 2, np.nan], [2, 3, 5, np.nan], [np.nan, 4, 6, np.nan]])>>> obj 0 1 2 30 1.0 NaN 2 NaN1 2.0 3.0 5 NaN2 NaN 4.0 6 NaN>>>>>> obj.dropna(axis='rows', thresh=3) 0 1 2 31 2.0 3.0 5 NaN 【02x04】fillna() 填充缺失值fillna() 方法可以将缺失值替换成有效的数值。 在 Series 对象中,fillna() 方法的语法如下: Series.fillna(self, value=None, method=None, axis=None, inplace=False, limit=None, downcast=None) 官方文档:https://pandas.pydata.org/docs/reference/api/pandas.Series.fillna.html 参数 描述 value 用于填充的值(例如 0),或者是一个 dict / Series / DataFrame 值指定要用于每个 index(对于 Series)或column(对于 DataFrame)的值不在dict / Series / DataFrame中的值将不被填充。此值不能是列表 method 填充方法:None‘pad’ / ‘ffill’:将上一个有效观测值向前传播到下一个有效观测值‘backfill’ / ‘bfill’:使用下一个有效观察值来填补空白 axis 0 or ‘index’,要填充缺失值的轴 123456789101112131415161718192021222324252627282930313233>>> import pandas as pd>>> obj = pd.Series([1, np.nan, 2, None, 3], index=list('abcde'))>>> obja 1.0b NaNc 2.0d NaNe 3.0dtype: float64>>> >>> obj.fillna(0)a 1.0b 0.0c 2.0d 0.0e 3.0dtype: float64>>> >>> obj.fillna(method='ffill')a 1.0b 1.0c 2.0d 2.0e 3.0dtype: float64>>> >>> obj.fillna(method='bfill')a 1.0b 2.0c 2.0d 3.0e 3.0dtype: float64 在 DataFrame 对象中,fillna() 方法的语法如下: DataFrame.fillna(self, value=None, method=None, axis=None, inplace=False, limit=None, downcast=None) 官方文档:https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.fillna.html 参数 描述 value 用于填充的值(例如 0),或者是一个 dict / Series / DataFrame 值指定要用于每个 index(对于 Series)或column(对于 DataFrame)的值不在dict / Series / DataFrame中的值将不被填充。此值不能是列表 method 填充方法:None‘pad’ / ‘ffill’:将上一个有效观测值向前传播到下一个有效观测值‘backfill’ / ‘bfill’:使用下一个有效观察值来填补空白 axis 0 or ‘index’,1 or ‘columns’,要填充缺失值的轴 在 DataFrame 对象中的用法和在 Series 对象中的用法大同小异,只不过 axis 参数多了一个选择: 1234567891011121314>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame([[1, np.nan, 2, np.nan], [2, 3, 5, np.nan], [np.nan, 4, 6, np.nan]])>>> obj 0 1 2 30 1.0 NaN 2 NaN1 2.0 3.0 5 NaN2 NaN 4.0 6 NaN>>> >>> obj.fillna(method='ffill', axis=1) 0 1 2 30 1.0 1.0 2.0 2.01 2.0 3.0 5.0 5.02 NaN 4.0 6.0 6.0 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106743778未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Pandas","slug":"Python-数据分析/Pandas","permalink":"https://www.itrhx.com/categories/Python-数据分析/Pandas/"}],"tags":[{"name":"Pandas","slug":"Pandas","permalink":"https://www.itrhx.com/tags/Pandas/"},{"name":"算术运算","slug":"算术运算","permalink":"https://www.itrhx.com/tags/算术运算/"},{"name":"缺失值","slug":"缺失值","permalink":"https://www.itrhx.com/tags/缺失值/"}]},{"title":"Python 数据分析三剑客之 Pandas(二):Index 索引对象以及各种索引操作","slug":"A80-Pandas-02","date":"2020-06-13T14:36:16.458Z","updated":"2020-07-06T13:44:26.916Z","comments":true,"path":"2020/06/13/A80-Pandas-02/","link":"","permalink":"https://www.itrhx.com/2020/06/13/A80-Pandas-02/","excerpt":"","text":"Pandas 系列文章: Python 数据分析三剑客之 Pandas(一):认识 Pandas 及其 Series、DataFrame 对象 Python 数据分析三剑客之 Pandas(二):Index 索引对象以及各种索引操作 Python 数据分析三剑客之 Pandas(三):算术运算与缺失值的处理 Python 数据分析三剑客之 Pandas(四):函数应用、映射、排序和层级索引 Python 数据分析三剑客之 Pandas(五):统计计算与统计描述 Python 数据分析三剑客之 Pandas(六):GroupBy 数据分裂、应用与合并 Python 数据分析三剑客之 Pandas(七):合并数据集 Python 数据分析三剑客之 Pandas(八):数据重塑、重复数据处理与数据替换 Python 数据分析三剑客之 Pandas(九):时间序列 Python 数据分析三剑客之 Pandas(十):数据读写 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106698307未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【1】Index 索引对象 Series 和 DataFrame 中的索引都是 Index 对象,为了保证数据的安全,索引对象是不可变的,如果尝试更改索引就会报错;常见的 Index 种类有:索引(Index),整数索引(Int64Index),层级索引(MultiIndex),时间戳类型(DatetimeIndex)。 一下代码演示了 Index 索引对象和其不可变的性质: 12345678910111213>>> import pandas as pd>>> obj = pd.Series([1, 5, -8, 2], index=['a', 'b', 'c', 'd'])>>> obj.indexIndex(['a', 'b', 'c', 'd'], dtype='object')>>> type(obj.index)<class 'pandas.core.indexes.base.Index'>>>> obj.index[0] = 'e'Traceback (most recent call last): File \"<pyshell#28>\", line 1, in <module> obj.index[0] = 'e' File \"C:\\Users\\...\\base.py\", line 3909, in __setitem__ raise TypeError(\"Index does not support mutable operations\")TypeError: Index does not support mutable operations index 索引对象常用属性 官方文档:https://pandas.pydata.org/docs/reference/api/pandas.Index.html 属性 描述 T 转置 array index 的数组形式,常见官方文档 dtype 返回基础数据的 dtype 对象 hasnans 是否有 NaN(缺失值) inferred_type 返回一个字符串,表示 index 的类型 is_monotonic 判断 index 是否是递增的 is_monotonic_decreasing 判断 index 是否单调递减 is_monotonic_increasing 判断 index 是否单调递增 is_unique index 是否没有重复值 nbytes 返回 index 中的字节数 ndim index 的维度 nlevels Number of levels. shape 返回一个元组,表示 index 的形状 size index 的大小 values 返回 index 中的值 / 数组 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748>>> import pandas as pd>>> obj = pd.Series([1, 5, -8, 2], index=['a', 'b', 'c', 'd'])>>> obj.indexIndex(['a', 'b', 'c', 'd'], dtype='object')>>> >>> obj.index.array<PandasArray>['a', 'b', 'c', 'd']Length: 4, dtype: object>>> >>> obj.index.dtypedtype('O')>>> >>> obj.index.hasnansFalse>>>>>> obj.index.inferred_type'string'>>> >>> obj.index.is_monotonicTrue>>>>>> obj.index.is_monotonic_decreasingFalse>>> >>> obj.index.is_monotonic_increasingTrue>>> >>> obj.index.is_uniqueTrue>>> >>> obj.index.nbytes16>>>>>> obj.index.ndim1>>>>>> obj.index.nlevels1>>>>>> obj.index.shape(4,)>>> >>> obj.index.size4>>> >>> obj.index.valuesarray(['a', 'b', 'c', 'd'], dtype=object) index 索引对象常用方法 官方文档:https://pandas.pydata.org/docs/reference/api/pandas.Index.html 方法 描述 all(self, *args, **kwargs) 判断所有元素是否为真,有 0 会被视为 False any(self, *args, **kwargs) 判断是否至少有一个元素为真,均为 0 会被视为 False append(self, other) 连接另一个 index,产生一个新的 index argmax(self[, axis, skipna]) 返回 index 中最大值的索引值 argmin(self[, axis, skipna]) 返回 index 中最小值的索引值 argsort(self, *args, **kwargs) 对 index 从小到大排序,返回排序后的元素在原 index 中的索引值 delete(self, loc) 删除指定索引位置的元素,返回删除后的新 index difference(self, other[, sort]) 在第一个 index 中删除第二个 index 中的元素,即差集 drop(self, labels[, errors]) 在原 index 中删除传入的值 drop_duplicates(self[, keep]) 删除重复值,keep 参数可选值如下:‘first’:保留第一次出现的重复项;‘last’:保留最后一次出现的重复项;False:不保留重复项 duplicated(self[, keep]) 判断是否为重复值,keep 参数可选值如下:‘first’:第一次重复的为 False,其他为 True;‘last’:最后一次重复的为 False,其他为 True;False:所有重复的均为 True dropna(self[, how]) 删除缺失值,即 NaN fillna(self[, value, downcast]) 用指定值填充缺失值,即 NaN equals(self, other) 判断两个 index 是否相同 insert(self, loc, item) 将元素插入到指定索引处,返回新的 index intersection(self, other[, sort]) 返回两个 index 的交集 isna(self) 检测 index 元素是否为缺失值,即 NaN isnull(self) 检测 index 元素是否为缺失值,即 NaN max(self[, axis, skipna]) 返回 index 的最大值 min(self[, axis, skipna]) 返回 index 的最小值 union(self, other[, sort]) 返回两个 index 的并集 unique(self[, level]) 返回 index 中的唯一值,相当于去除重复值 all(self, *args, **kwargs) 【官方文档】 123456>>> import pandas as pd>>> pd.Index([1, 2, 3]).all()True>>>>>> pd.Index([0, 1, 2]).all()False any(self, *args, **kwargs) 【官方文档】 123456>>> import pandas as pd>>> pd.Index([0, 0, 1]).any()True>>>>>> pd.Index([0, 0, 0]).any()False append(self, other) 【官方文档】 123>>> import pandas as pd>>> pd.Index(['a', 'b', 'c']).append(pd.Index([1, 2, 3]))Index(['a', 'b', 'c', 1, 2, 3], dtype='object') argmax(self[, axis, skipna]) 【官方文档】 123>>> import pandas as pd>>> pd.Index([5, 2, 3, 9, 1]).argmax()3 argmin(self[, axis, skipna]) 【官方文档】 123>>> import pandas as pd>>> pd.Index([5, 2, 3, 9, 1]).argmin()4 argsort(self, *args, **kwargs) 【官方文档】 123>>> import pandas as pd>>> pd.Index([5, 2, 3, 9, 1]).argsort()array([4, 1, 2, 0, 3], dtype=int32) delete(self, loc) 【官方文档】 123>>> import pandas as pd>>> pd.Index([5, 2, 3, 9, 1]).delete(0)Int64Index([2, 3, 9, 1], dtype='int64') difference(self, other[, sort]) 【官方文档】 1234567>>> import pandas as pd>>> idx1 = pd.Index([2, 1, 3, 4])>>> idx2 = pd.Index([3, 4, 5, 6])>>> idx1.difference(idx2)Int64Index([1, 2], dtype='int64')>>> idx1.difference(idx2, sort=False)Int64Index([2, 1], dtype='int64') drop(self, labels[, errors]) 【官方文档】 123>>> import pandas as pd>>> pd.Index([5, 2, 3, 9, 1]).drop([2, 1])Int64Index([5, 3, 9], dtype='int64') drop_duplicates(self[, keep]) 【官方文档】 12345678>>> import pandas as pd>>> idx = pd.Index(['lama', 'cow', 'lama', 'beetle', 'lama', 'hippo'])>>> idx.drop_duplicates(keep='first')Index(['lama', 'cow', 'beetle', 'hippo'], dtype='object')>>> idx.drop_duplicates(keep='last')Index(['cow', 'beetle', 'lama', 'hippo'], dtype='object')>>> idx.drop_duplicates(keep=False)Index(['cow', 'beetle', 'hippo'], dtype='object') duplicated(self[, keep]) 【官方文档】 12345678910>>> import pandas as pd>>> idx = pd.Index(['lama', 'cow', 'lama', 'beetle', 'lama'])>>> idx.duplicated()array([False, False, True, False, True])>>> idx.duplicated(keep='first')array([False, False, True, False, True])>>> idx.duplicated(keep='last')array([ True, False, True, False, False])>>> idx.duplicated(keep=False)array([ True, False, True, False, True]) dropna(self[, how]) 【官方文档】 1234>>> import numpy as np>>> import pandas as pd>>> pd.Index([2, 5, np.NaN, 6, np.NaN, np.NaN]).dropna()Float64Index([2.0, 5.0, 6.0], dtype='float64') fillna(self[, value, downcast]) 【官方文档】 1234>>> import numpy as np>>> import pandas as pd>>> pd.Index([2, 5, np.NaN, 6, np.NaN, np.NaN]).fillna(5)Float64Index([2.0, 5.0, 5.0, 6.0, 5.0, 5.0], dtype='float64') equals(self, other) 【官方文档】 12345678910>>> import pandas as pd>>> idx1 = pd.Index([5, 2, 3, 9, 1])>>> idx2 = pd.Index([5, 2, 3, 9, 1])>>> idx1.equals(idx2)True>>> >>> idx1 = pd.Index([5, 2, 3, 9, 1])>>> idx2 = pd.Index([5, 2, 4, 9, 1])>>> idx1.equals(idx2)False intersection(self, other[, sort]) 【官方文档】 12345>>> import pandas as pd>>> idx1 = pd.Index([1, 2, 3, 4])>>> idx2 = pd.Index([3, 4, 5, 6])>>> idx1.intersection(idx2)Int64Index([3, 4], dtype='int64') insert(self, loc, item) 【官方文档】 123>>> import pandas as pd>>> pd.Index([5, 2, 3, 9, 1]).insert(2, 'A')Index([5, 2, 'A', 3, 9, 1], dtype='object') isna(self) 【官方文档】、isnull(self) 【官方文档】 123456>>> import numpy as np>>> import pandas as pd>>> pd.Index([2, 5, np.NaN, 6, np.NaN, np.NaN]).isna()array([False, False, True, False, True, True])>>> pd.Index([2, 5, np.NaN, 6, np.NaN, np.NaN]).isnull()array([False, False, True, False, True, True]) max(self[, axis, skipna]) 【官方文档】、min(self[, axis, skipna]) 【官方文档】 12345>>> import pandas as pd>>> pd.Index([5, 2, 3, 9, 1]).max()9>>> pd.Index([5, 2, 3, 9, 1]).min()1 union(self, other[, sort]) 【官方文档】 12345>>> import pandas as pd>>> idx1 = pd.Index([1, 2, 3, 4])>>> idx2 = pd.Index([3, 4, 5, 6])>>> idx1.union(idx2)Int64Index([1, 2, 3, 4, 5, 6], dtype='int64') unique(self[, level]) 【官方文档】 123>>> import pandas as pd>>> pd.Index([5, 1, 3, 5, 1]).unique()Int64Index([5, 1, 3], dtype='int64') 【2】Pandas 一般索引由于在 Pandas 中,由于有一些更高级的索引操作,比如重新索引,层级索引等,因此将一般的切片索引、花式索引、布尔索引等归纳为一般索引。 【2.1】Series 索引【2.1.1】head() / tail()Series.head() 和 Series.tail() 方法可以获取的前五行和后五行数据,如果向 head() / tail() 里面传入参数,则会获取指定行: 1234567891011121314151617181920212223242526272829303132333435363738394041>>> import pandas as pd>>> import numpy as np>>> obj = pd.Series(np.random.randn(8))>>> obj0 -0.6434371 -0.3656522 -0.9665543 -0.0361274 1.0460955 -2.0483626 -1.8655517 1.344728dtype: float64>>> >>> obj.head()0 -0.6434371 -0.3656522 -0.9665543 -0.0361274 1.046095dtype: float64>>> >>> obj.head(3)0 -0.6434371 -0.3656522 -0.966554dtype: float64>>>>>> obj.tail()3 1.2212214 -1.3734965 1.0328436 0.0297347 -1.861485dtype: float64>>>>>> obj.tail(3)5 1.0328436 0.0297347 -1.861485dtype: float64 【2.1.2】行索引Pandas 中可以按照位置进行索引,也可以按照索引名(index)进行索引,也可以用 Python 字典的表达式和方法来获取值: 123456789101112131415161718>>> import pandas as pd>>> obj = pd.Series([1, 5, -8, 2], index=['a', 'b', 'c', 'd'])>>> obja 1b 5c -8d 2dtype: int64>>> obj['c']-8>>> obj[2]-8>>> 'b' in objTrue>>> obj.keys()Index(['a', 'b', 'c', 'd'], dtype='object')>>> list(obj.items())[('a', 1), ('b', 5), ('c', -8), ('d', 2)] 【2.1.3】切片索引切片的方法有两种:按位置切片和按索引名(index)切片,注意:按位置切片时,不包含终止索引;按索引名(index)切片时,包含终止索引。 123456789101112131415161718192021222324>>> import pandas as pd>>> obj = pd.Series([1, 5, -8, 2], index=['a', 'b', 'c', 'd'])>>> obja 1b 5c -8d 2dtype: int64>>>>>> obj[1:3]b 5c -8dtype: int64>>>>>> obj[0:3:2]a 1c -8dtype: int64>>>>>> obj['b':'d']b 5c -8d 2dtype: int64 【2.1.4】花式索引所谓的花式索引,就是间隔索引、不连续的索引,传递一个由索引名(index)或者位置参数组成的列表来一次性获得多个元素: 12345678910111213141516171819>>> import pandas as pd>>> obj = pd.Series([1, 5, -8, 2], index=['a', 'b', 'c', 'd'])>>> obja 1b 5c -8d 2dtype: int64>>> >>> obj[[0, 2]]a 1c -8dtype: int64>>> >>> obj[['a', 'c', 'd']]a 1c -8d 2dtype: int64 【2.1.5】布尔索引可以通过一个布尔数组来索引目标数组,即通过布尔运算(如:比较运算符)来获取符合指定条件的元素的数组。 1234567891011121314151617181920212223>>> import pandas as pd>>> obj = pd.Series([1, 5, -8, 2, -3], index=['a', 'b', 'c', 'd', 'e'])>>> obja 1b 5c -8d 2e -3dtype: int64>>> >>> obj[obj > 0]a 1b 5d 2dtype: int64>>> >>> obj > 0a Trueb Truec Falsed Truee Falsedtype: bool 【2.2】DataFrame 索引【2.2.1】head() / tail()和 Series 一样,DataFrame.head() 和 DataFrame.tail() 方法同样可以获取 DataFrame 的前五行和后五行数据,如果向 head() / tail() 里面传入参数,则会获取指定行: 1234567891011121314151617181920212223242526272829303132333435363738394041>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame(np.random.randn(8,4), columns = ['a', 'b', 'c', 'd'])>>> obj a b c d0 -1.399390 0.521596 -0.869613 0.5066211 -0.748562 -0.364952 0.188399 -1.4025662 1.378776 -1.476480 0.361635 0.4511343 -0.206405 -1.188609 3.002599 0.5636504 0.993289 1.133748 1.177549 -2.5622865 -0.482157 1.069293 1.143983 -1.3030796 -1.199154 0.220360 0.801838 -0.1045337 -1.359816 -2.092035 2.003530 -0.151812>>> >>> obj.head() a b c d0 -1.399390 0.521596 -0.869613 0.5066211 -0.748562 -0.364952 0.188399 -1.4025662 1.378776 -1.476480 0.361635 0.4511343 -0.206405 -1.188609 3.002599 0.5636504 0.993289 1.133748 1.177549 -2.562286>>> >>> obj.head(3) a b c d0 -1.399390 0.521596 -0.869613 0.5066211 -0.748562 -0.364952 0.188399 -1.4025662 1.378776 -1.476480 0.361635 0.451134>>>>>> obj.tail() a b c d3 -0.206405 -1.188609 3.002599 0.5636504 0.993289 1.133748 1.177549 -2.5622865 -0.482157 1.069293 1.143983 -1.3030796 -1.199154 0.220360 0.801838 -0.1045337 -1.359816 -2.092035 2.003530 -0.151812>>> >>> obj.tail(3) a b c d5 -0.482157 1.069293 1.143983 -1.3030796 -1.199154 0.220360 0.801838 -0.1045337 -1.359816 -2.092035 2.003530 -0.151812 【2.2.2】列索引DataFrame 可以按照列标签(columns)来进行列索引: 12345678910111213141516171819202122232425262728293031323334353637>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame(np.random.randn(7,2), columns = ['a', 'b'])>>> obj a b0 -1.198795 0.9283781 -2.878230 0.0146502 2.267475 0.3709523 0.639340 -1.3010414 -1.953444 0.1489345 -0.445225 0.4596326 0.097109 -2.592833>>>>>> obj['a']0 -1.1987951 -2.8782302 2.2674753 0.6393404 -1.9534445 -0.4452256 0.097109Name: a, dtype: float64>>> >>> obj[['a']] a0 -1.1987951 -2.8782302 2.2674753 0.6393404 -1.9534445 -0.4452256 0.097109>>> >>> type(obj['a'])<class 'pandas.core.series.Series'>>>> type(obj[['a']])<class 'pandas.core.frame.DataFrame'> 【2.2.3】切片索引DataFrame 中的切片索引是针对行来操作的,切片的方法有两种:按位置切片和按索引名(index)切片,注意:按位置切片时,不包含终止索引;按索引名(index)切片时,包含终止索引。 123456789101112131415161718192021222324252627282930>>> import pandas as pd>>> import numpy as np>>> data = np.random.randn(5,4)>>> index = ['I1', 'I2', 'I3', 'I4', 'I5']>>> columns = ['a', 'b', 'c', 'd']>>> obj = pd.DataFrame(data, index, columns)>>> obj a b c dI1 0.828676 -1.663337 1.753632 1.432487I2 0.368138 0.222166 0.902764 -1.436186I3 2.285615 -2.415175 -1.344456 -0.502214I4 3.224288 -0.500268 1.293596 -1.235549I5 -0.938833 -0.804433 -0.170047 -0.566766>>> >>> obj[0:3] a b c dI1 0.828676 -1.663337 1.753632 1.432487I2 0.368138 0.222166 0.902764 -1.436186I3 2.285615 -2.415175 -1.344456 -0.502214>>>>>> obj[0:4:2] a b c dI1 -0.042168 1.437354 -1.114545 0.830790I3 0.241506 0.018984 -0.499151 -1.190143>>>>>> obj['I2':'I4'] a b c dI2 0.368138 0.222166 0.902764 -1.436186I3 2.285615 -2.415175 -1.344456 -0.502214I4 3.224288 -0.500268 1.293596 -1.235549 【2.2.4】花式索引和 Series 一样,所谓的花式索引,就是间隔索引、不连续的索引,传递一个由列名(columns)组成的列表来一次性获得多列元素: 123456789101112131415161718192021>>> import pandas as pd>>> import numpy as np>>> data = np.random.randn(5,4)>>> index = ['I1', 'I2', 'I3', 'I4', 'I5']>>> columns = ['a', 'b', 'c', 'd']>>> obj = pd.DataFrame(data, index, columns)>>> obj a b c dI1 -1.083223 -0.182874 -0.348460 -1.572120I2 -0.205206 -0.251931 1.180131 0.847720I3 -0.980379 0.325553 -0.847566 -0.882343I4 -0.638228 -0.282882 -0.624997 -0.245980I5 -0.229769 1.002930 -0.226715 -0.916591>>> >>> obj[['a', 'd']] a dI1 -1.083223 -1.572120I2 -0.205206 0.847720I3 -0.980379 -0.882343I4 -0.638228 -0.245980I5 -0.229769 -0.916591 【2.2.5】布尔索引可以通过一个布尔数组来索引目标数组,即通过布尔运算(如:比较运算符)来获取符合指定条件的元素的数组。 1234567891011121314151617181920212223242526272829>>> import pandas as pd>>> import numpy as np>>> data = np.random.randn(5,4)>>> index = ['I1', 'I2', 'I3', 'I4', 'I5']>>> columns = ['a', 'b', 'c', 'd']>>> obj = pd.DataFrame(data, index, columns)>>> obj a b c dI1 -0.602984 -0.135716 0.999689 -0.339786I2 0.911130 -0.092485 -0.914074 -0.279588I3 0.849606 -0.420055 -1.240389 -0.179297I4 0.249986 -1.250668 0.329416 -1.105774I5 -0.743816 0.430647 -0.058126 -0.337319>>> >>> obj[obj > 0] a b c dI1 NaN NaN 0.999689 NaNI2 0.911130 NaN NaN NaNI3 0.849606 NaN NaN NaNI4 0.249986 NaN 0.329416 NaNI5 NaN 0.430647 NaN NaN>>> >>> obj > 0 a b c dI1 False False True FalseI2 True False False FalseI3 True False False FalseI4 True False True FalseI5 False True False False 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106698307未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【3】索引器:loc 和 ilocloc 是标签索引、iloc 是位置索引,注意:在 Pandas1.0.0 之前还有 ix 方法(即可按标签也可按位置索引),在 Pandas1.0.0 之后已被移除。 【3.1】loc 标签索引loc 标签索引,即根据 index 和 columns 来选择数据。 【3.1.1】Series.loc在 Series 中,允许输入: 单个标签,例如 5 或 'a',(注意,5 是 index 的名称,而不是位置索引); 标签列表或数组,例如 ['a', 'b', 'c']; 带有标签的切片对象,例如 'a':'f'。 官方文档:https://pandas.pydata.org/docs/reference/api/pandas.Series.loc.html 12345678910111213141516171819202122>>> import pandas as np>>> obj = pd.Series([1, 5, -8, 2], index=['a', 'b', 'c', 'd'])>>> obja 1b 5c -8d 2dtype: int64>>> >>> obj.loc['a']1>>> >>> obj.loc['a':'c']a 1b 5c -8dtype: int64>>>>>> obj.loc[['a', 'd']]a 1d 2dtype: int64 【3.1.2】DataFrame.loc在 DataFrame 中,第一个参数索引行,第二个参数是索引列,允许输入的格式和 Series 大同小异。 官方文档:https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.loc.html 1234567891011121314151617181920212223242526272829303132>>> import pandas as pd>>> obj = pd.DataFrame([[1, 2, 3], [4, 5, 6], [7, 8, 9]], index=['a', 'b', 'c'], columns=['A', 'B', 'C'])>>> obj A B Ca 1 2 3b 4 5 6c 7 8 9>>> >>> obj.loc['a']A 1B 2C 3Name: a, dtype: int64>>> >>> obj.loc['a':'c'] A B Ca 1 2 3b 4 5 6c 7 8 9>>> >>> obj.loc[['a', 'c']] A B Ca 1 2 3c 7 8 9>>> >>> obj.loc['b', 'B']5>>> obj.loc['b', 'A':'C']A 4B 5C 6Name: b, dtype: int64 【3.2】iloc 位置索引作用和 loc 一样,不过是基于索引的编号来索引,即根据 index 和 columns 的位置编号来选择数据。 【3.2.1】Series.iloc官方文档:https://pandas.pydata.org/docs/reference/api/pandas.Series.iloc.html 在 Series 中,允许输入: 整数,例如 5; 整数列表或数组,例如 [4, 3, 0]; 具有整数的切片对象,例如 1:7。 12345678910111213141516171819202122>>> import pandas as np>>> obj = pd.Series([1, 5, -8, 2], index=['a', 'b', 'c', 'd'])>>> obja 1b 5c -8d 2dtype: int64>>> >>> obj.iloc[1]5>>> >>> obj.iloc[0:2]a 1b 5dtype: int64>>> >>> obj.iloc[[0, 1, 3]]a 1b 5d 2dtype: int64 【3.2.2】DataFrame.iloc官方文档:https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.iloc.html 在 DataFrame 中,第一个参数索引行,第二个参数是索引列,允许输入的格式和 Series 大同小异: 12345678910111213141516171819202122232425262728293031>>> import pandas as pd>>> obj = pd.DataFrame([[1, 2, 3], [4, 5, 6], [7, 8, 9]], index=['a', 'b', 'c'], columns=['A', 'B', 'C'])>>> obj A B Ca 1 2 3b 4 5 6c 7 8 9>>> >>> obj.iloc[1]A 4B 5C 6Name: b, dtype: int64>>> >>> obj.iloc[0:2] A B Ca 1 2 3b 4 5 6>>> >>> obj.iloc[[0, 2]] A B Ca 1 2 3c 7 8 9>>> >>> obj.iloc[1, 2]6>>> >>> obj.iloc[1, 0:2]A 4B 5Name: b, dtype: int64 【4】Pandas 重新索引Pandas 对象的一个重要方法是 reindex,其作用是创建一个新对象,它的数据符合新的索引。以 DataFrame.reindex 为例(Series 类似),基本语法如下: DataFrame.reindex(self, labels=None, index=None, columns=None, axis=None, method=None, copy=True, level=None, fill_value=nan, limit=None, tolerance=None) 部分参数描述如下:(完整参数解释参见官方文档) 参数 描述 index 用作索引的新序列,既可以是 index 实例,也可以是其他序列型的 Python 数据结构 method 插值(填充)方式,取值如下:None:不填补空白;pad / ffill:将上一个有效的观测值向前传播到下一个有效的观测值;backfill / bfill:使用下一个有效观察值来填补空白;nearest:使用最近的有效观测值来填补空白。 fill_value 在重新索引的过程中,需要引入缺失值时使用的替代值 limit 前向或后向填充时的最大填充量 tolerance 向前或向后填充时,填充不准确匹配项的最大间距(绝对值距离) level 在 Multilndex 的指定级别上匹配简单索引,否则选其子集 copy 默认为 True,无论如何都复制;如果为 False,则新旧相等就不复制 reindex 将会根据新索引进行重排。如果某个索引值当前不存在,就引入缺失值: 1234567891011121314151617>>> import pandas as pd>>> obj = pd.Series([4.5, 7.2, -5.3, 3.6], index=['d', 'b', 'a', 'c'])>>> objd 4.5b 7.2a -5.3c 3.6dtype: float64>>> >>> obj2 = obj.reindex(['a', 'b', 'c', 'd', 'e'])>>> obj2a -5.3b 7.2c 3.6d 4.5e NaNdtype: float64 对于时间序列这样的有序数据,重新索引时可能需要做一些插值处理。method 选项即可达到此目的,例如,使用 ffill 可以实现前向值填充: 1234567891011121314151617>>> import pandas as pd>>> obj = pd.Series(['blue', 'purple', 'yellow'], index=[0, 2, 4])>>> obj0 blue2 purple4 yellowdtype: object>>> >>> obj2 = obj.reindex(range(6), method='ffill')>>> obj20 blue1 blue2 purple3 purple4 yellow5 yellowdtype: object 借助 DataFrame,reindex可以修改(行)索引和列。只传递一个序列时,会重新索引结果的行: 12345678910111213141516>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame(np.arange(9).reshape((3, 3)), index=['a', 'c', 'd'], columns=['Ohio', 'Texas', 'California'])>>> obj Ohio Texas Californiaa 0 1 2c 3 4 5d 6 7 8>>> >>> obj2 = obj.reindex(['a', 'b', 'c', 'd'])>>> obj2 Ohio Texas Californiaa 0.0 1.0 2.0b NaN NaN NaNc 3.0 4.0 5.0d 6.0 7.0 8.0 列可以用 columns 关键字重新索引: 123456789101112131415>>> import pandas as pd>>> import numpy as np>>> obj = pd.DataFrame(np.arange(9).reshape((3, 3)), index=['a', 'c', 'd'], columns=['Ohio', 'Texas', 'California'])>>> obj Ohio Texas Californiaa 0 1 2c 3 4 5d 6 7 8>>> >>> states = ['Texas', 'Utah', 'California']>>> obj.reindex(columns=states) Texas Utah Californiaa 1 NaN 2c 4 NaN 5d 7 NaN 8 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106698307未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Pandas","slug":"Python-数据分析/Pandas","permalink":"https://www.itrhx.com/categories/Python-数据分析/Pandas/"}],"tags":[{"name":"索引","slug":"索引","permalink":"https://www.itrhx.com/tags/索引/"},{"name":"Pandas","slug":"Pandas","permalink":"https://www.itrhx.com/tags/Pandas/"},{"name":"Index","slug":"Index","permalink":"https://www.itrhx.com/tags/Index/"}]},{"title":"Python 数据分析三剑客之 Pandas(一):认识 Pandas 及其 Series、DataFrame 对象","slug":"A79-Pandas-01","date":"2020-06-11T12:46:27.848Z","updated":"2020-08-06T03:30:39.925Z","comments":true,"path":"2020/06/11/A79-Pandas-01/","link":"","permalink":"https://www.itrhx.com/2020/06/11/A79-Pandas-01/","excerpt":"","text":"Pandas 系列文章: Python 数据分析三剑客之 Pandas(一):认识 Pandas 及其 Series、DataFrame 对象 Python 数据分析三剑客之 Pandas(二):Index 索引对象以及各种索引操作 Python 数据分析三剑客之 Pandas(三):算术运算与缺失值的处理 Python 数据分析三剑客之 Pandas(四):函数应用、映射、排序和层级索引 Python 数据分析三剑客之 Pandas(五):统计计算与统计描述 Python 数据分析三剑客之 Pandas(六):GroupBy 数据分裂、应用与合并 Python 数据分析三剑客之 Pandas(七):合并数据集 Python 数据分析三剑客之 Pandas(八):数据重塑、重复数据处理与数据替换 Python 数据分析三剑客之 Pandas(九):时间序列 Python 数据分析三剑客之 Pandas(十):数据读写 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106676693未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【01x00】了解 PandasPandas 是 Python 的一个数据分析包,是基于 NumPy 构建的,最初由 AQR Capital Management 于 2008 年 4 月开发,并于 2009 年底开源出来,目前由专注于 Python 数据包开发的 PyData 开发团队继续开发和维护,属于 PyData 项目的一部分。 Pandas 最初被作为金融数据分析工具而开发出来,因此,Pandas 为时间序列分析提供了很好的支持。Pandas 的名称来自于面板数据(panel data)和 Python 数据分析(data analysis)。panel data 是经济学中关于多维数据集的一个术语,在 Pandas 中也提供了 panel 的数据类型。 Pandas 经常和其它工具一同使用,如数值计算工具 NumPy 和 SciPy,分析库 statsmodels 和 scikit-learn,数据可视化库 Matplotlib 等,虽然 Pandas 采用了大量的 NumPy 编码风格,但二者最大的不同是 Pandas 是专门为处理表格和混杂数据设计的。而 NumPy 更适合处理统一的数值数组数据。 【以下对 Pandas 的解释翻译自官方文档:https://pandas.pydata.org/docs/getting_started/overview.html#package-overview】 Pandas 是 Python 的核心数据分析支持库,提供了快速、灵活、明确的数据结构,旨在简单、直观地处理关系型、标记型数据。Pandas 的目标是成为 Python 数据分析实践与实战的必备高级工具,其长远目标是成为最强大、最灵活、可以支持任何语言的开源数据分析工具。经过多年不懈的努力,Pandas 离这个目标已经越来越近了。 Pandas 适用于处理以下类型的数据: 与 SQL 或 Excel 表类似的,含异构列的表格数据; 有序和无序(非固定频率)的时间序列数据; 带行列标签的矩阵数据,包括同构或异构型数据; 任意其它形式的观测、统计数据集, 数据转入 Pandas 数据结构时不必事先标记。 Pandas 的主要数据结构是 Series(一维数据)与 DataFrame(二维数据),这两种数据结构足以处理- 金融、统计、社会科学、工程等领域里的大多数典型用例。对于 R 语言用户,DataFrame 提供了比 R 语言 data.frame 更丰富的功能。Pandas 基于 NumPy 开发,可以与其它第三方科学计算支持库完美集成。 Pandas 就像一把万能瑞士军刀,下面仅列出了它的部分优势 : 处理浮点与非浮点数据里的缺失数据,表示为 NaN; 大小可变:插入或删除 DataFrame 等多维对象的列; 自动、显式数据对齐:显式地将对象与一组标签对齐,也可以忽略标签,在 Series、DataFrame 计算时自动与数据对齐; 强大、灵活的分组(group by)功能:拆分-应用-组合数据集,聚合、转换数据; 把 Python 和 NumPy 数据结构里不规则、不同索引的数据轻松地转换为 DataFrame 对象; 基于智能标签,对大型数据集进行切片、花式索引、子集分解等操作; 直观地合并和连接数据集; 灵活地重塑和旋转数据集; 轴支持分层标签(每个刻度可能有多个标签); 强大的 IO 工具,读取平面文件(CSV 等支持分隔符的文件)、Excel 文件、数据库等来源的数据,以及从超快 HDF5 格式保存 / 加载数据; 时间序列:支持日期范围生成、频率转换、移动窗口统计、移动窗口线性回归、日期位移等时间序列功能。 这些功能主要是为了解决其它编程语言、科研环境的痛点。处理数据一般分为几个阶段:数据整理与清洗、数据分析与建模、数据可视化与制表,Pandas 是处理数据的理想工具。 其它说明: Pandas 速度很快。Pandas 的很多底层算法都用 Cython 优化过。然而,为了保持通用性,必然要牺牲一些性能,如果专注某一功能,完全可以开发出比 Pandas 更快的专用工具。 Pandas 是 statsmodels 的依赖项,因此,Pandas 也是 Python 中统计计算生态系统的重要组成部分。 Pandas 已广泛应用于金融领域。 【02x00】Pandas 数据结构Pandas 的主要数据结构是 Series(带标签的一维同构数组)与 DataFrame(带标签的,大小可变的二维异构表格)。 Pandas 数据结构就像是低维数据的容器。比如,DataFrame 是 Series 的容器,Series 则是标量的容器。使用这种方式,可以在容器中以字典的形式插入或删除对象。 此外,通用 API 函数的默认操作要顾及时间序列与截面数据集的方向。当使用 Ndarray 存储二维或三维数据时,编写函数要注意数据集的方向,这对用户来说是一种负担;如果不考虑 C 或 Fortran 中连续性对性能的影响,一般情况下,不同的轴在程序里其实没有什么区别。Pandas 里,轴的概念主要是为了给数据赋予更直观的语义,即用更恰当的方式表示数据集的方向。这样做可以让用户编写数据转换函数时,少费点脑子。 处理 DataFrame 等表格数据时,对比 Numpy,index(行)或 columns(列)比 axis 0 和 axis 1 更直观。用这种方式迭代 DataFrame 的列,代码更易读易懂: 123for col in df.columns: series = df[col] # do something with series 【03x00】Series 对象Series 是带标签的一维数组,可存储整数、浮点数、字符串、Python 对象等类型的数据。轴标签统称为索引。调用 pandas.Series 函数即可创建 Series,基本语法如下: pandas.Series(data=None[, index=None, dtype=None, name=None, copy=False, fastpath=False]) 参数 描述 data 数组类型,可迭代的,字典或标量值,存储在序列中的数据 index 索引(数据标签),值必须是可哈希的,并且具有与数据相同的长度,允许使用非唯一索引值。如果未提供,将默认为RangeIndex(0,1,2,…,n) dtype 输出系列的数据类型。可选项,如果未指定,则将从数据中推断,具体参考官网 dtypes 介绍 name str 类型,可选项,给 Series 命名 copy bool 类型,可选项,默认 False,是否复制输入数据 【03x01】通过 list 构建 Series一般情况下我们只会用到 data 和 index 参数,可以通过 list(列表) 构建 Series,示例如下: 12345678>>> import pandas as pd>>> obj = pd.Series([1, 5, -8, 2])>>> obj0 11 52 -83 2dtype: int64 由于我们没有为数据指定索引,于是会自动创建一个 0 到 N-1(N 为数据的长度)的整数型索引,左边一列是自动创建的索引(index),右边一列是数据(data)。 此外,还可以自定义索引(index): 12345678>>> import pandas as pd>>> obj = pd.Series([1, 5, -8, 2], index=['a', 'b', 'c', 'd'])>>> obja 1b 5c -8d 2dtype: int64 索引(index)也可以通过赋值的方式就地修改: 123456789101112131415>>> import pandas as pd>>> obj = pd.Series([1, 5, -8, 2], index=['a', 'b', 'c', 'd'])>>> obja 1b 5c -8d 2dtype: int64>>> obj.index = ['Bob', 'Steve', 'Jeff', 'Ryan']>>> objBob 1Steve 5Jeff -8Ryan 2dtype: int64 【03x02】通过 dict 构建 Series通过 字典(dict) 构建 Series,字典的键(key)会作为索引(index),字典的值(value)会作为数据(data),示例如下: 123456789>>> import pandas as pd>>> data = {'Beijing': 21530000, 'Shanghai': 24280000, 'Wuhan': 11210000, 'Zhejiang': 58500000}>>> obj = pd.Series(data)>>> objBeijing 21530000Shanghai 24280000Wuhan 11210000Zhejiang 58500000dtype: int64 如果你想按照某个特定的顺序输出结果,可以传入排好序的字典的键以改变顺序: 1234567891011>>> import pandas as pd>>> data = {'Beijing': 21530000, 'Shanghai': 24280000, 'Wuhan': 11210000, 'Zhejiang': 58500000}>>> cities = ['Guangzhou', 'Wuhan', 'Zhejiang', 'Shanghai']>>> obj = pd.Series(data, index=cities)>>> objGuangzhou NaNWuhan 11210000.0Zhejiang 58500000.0Shanghai 24280000.0dtype: float64 注意:data 为字典,且未设置 index 参数时: 如果 Python >= 3.6 且 Pandas >= 0.23,Series 按字典的插入顺序排序索引。 如果 Python < 3.6 或 Pandas < 0.23,Series 按字母顺序排序索引。 【03x03】获取其数据和索引我们可以通过 Series 的 values 和 index 属性获取其数据和索引对象: 123456>>> import pandas as pd>>> obj = pd.Series([1, 5, -8, 2], index=['a', 'b', 'c', 'd'])>>> obj.valuesarray([ 1, 5, -8, 2], dtype=int64)>>> obj.indexIndex(['a', 'b', 'c', 'd'], dtype='object') 【03x04】通过索引获取数据与普通 NumPy 数组相比,Pandas 可以通过索引的方式选取 Series 中的单个或一组值,获取一组值时,传入的是一个列表,列表中的元素是索引值,另外还可以通过索引来修改其对应的值: 12345678910111213141516>>> import pandas as pd>>> obj = pd.Series([1, 5, -8, 2], index=['a', 'b', 'c', 'd'])>>> obja 1b 5c -8d 2dtype: int64>>> obj['a']1>>> obj['a'] = 3>>> obj[['a', 'b', 'c']]a 3b 5c -8dtype: int64 【03x05】使用函数运算在 Pandas 中可以使用 NumPy 函数或类似 NumPy 的运算(如根据布尔型数组进行过滤、标量乘法、应用数学函数等): 1234567891011121314151617181920>>> import pandas as pd>>> import numpy as np>>> obj = pd.Series([1, 5, -8, 2], index=['a', 'b', 'c', 'd'])>>> obj[obj > 0]a 1b 5d 2dtype: int64>>> obj * 2a 2b 10c -16d 4dtype: int64>>> np.exp(obj)a 2.718282b 148.413159c 0.000335d 7.389056dtype: float64 除了这些运算函数以外,还可以将 Series 看成是一个定长的有序字典,因为它是索引值到数据值的一个映射。它可以用在许多原本需要字典参数的函数中: 123456>>> import pandas as pd>>> obj = pd.Series([1, 5, -8, 2], index=['a', 'b', 'c', 'd'])>>> 'a' in objTrue>>> 'e' in objFalse 和 NumPy 类似,Pandas 中也有 NaN(即非数字,not a number),在 Pandas 中,它用于表示缺失值,Pandas 的 isnull 和 notnull 函数可用于检测缺失数据: 123456789101112131415161718192021222324252627282930313233>>> import pandas as pd>>> import numpy as np>>> obj = pd.Series([np.NaN, 5, -8, 2], index=['a', 'b', 'c', 'd'])>>> obja NaNb 5.0c -8.0d 2.0dtype: float64>>> pd.isnull(obj)a Trueb Falsec Falsed Falsedtype: bool>>> pd.notnull(obj)a Falseb Truec Trued Truedtype: bool>>> obj.isnull()a Trueb Falsec Falsed Falsedtype: bool>>> obj.notnull()a Falseb Truec Trued Truedtype: bool 【03x06】name 属性可以在 pandas.Series 方法中为 Series 对象指定一个 name: 123456789>>> import pandas as pd>>> data = {'Beijing': 21530000, 'Shanghai': 24280000, 'Wuhan': 11210000, 'Zhejiang': 58500000}>>> obj = pd.Series(data, name='population')>>> objBeijing 21530000Shanghai 24280000Wuhan 11210000Zhejiang 58500000Name: population, dtype: int64 也可以通过 name 和 index.name 属性为 Series 对象和其索引指定 name: 123456789101112>>> import pandas as pd>>> data = {'Beijing': 21530000, 'Shanghai': 24280000, 'Wuhan': 11210000, 'Zhejiang': 58500000}>>> obj = pd.Series(data)>>> obj.name = 'population'>>> obj.index.name = 'cities'>>> objcitiesBeijing 21530000Shanghai 24280000Wuhan 11210000Zhejiang 58500000Name: population, dtype: int64 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106676693未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【04x00】DataFrame 对象DataFrame 是一个表格型的数据结构,它含有一组有序的列,每列可以是不同的值类型(数值、字符串、布尔值等)。DataFrame 既有行索引也有列索引,它可以被看做由 Series 组成的字典(共用同一个索引)。DataFrame 中的数据是以一个或多个二维块存放的(而不是列表、字典或别的一维数据结构)。 类似多维数组/表格数据 (如Excel、R 语言中的 data.frame); 每列数据可以是不同的类型; 索引包括列索引和行索引 基本语法如下: pandas.DataFrame(data=None, index: Optional[Collection] = None, columns: Optional[Collection] = None, dtype: Union[str, numpy.dtype, ExtensionDtype, None] = None, copy: bool = False) 参数 描述 data ndarray 对象(结构化或同类的)、可迭代的或者字典形式,存储在序列中的数据 index 数组类型,索引(数据标签),如果未提供,将默认为 RangeIndex(0,1,2,…,n) columns 列标签。如果未提供,则将默认为 RangeIndex(0、1、2、…、n) dtype 输出系列的数据类型。可选项,如果未指定,则将从数据中推断,具体参考官网 dtypes 介绍 copy bool 类型,可选项,默认 False,是否复制输入数据,仅影响 DataFrame/2d ndarray 输入 【03x01】通过 ndarray 构建 DataFrame1234567891011121314151617>>> import numpy as np>>> import pandas as pd>>> data = np.random.randn(5,3)>>> dataarray([[-2.16231157, 0.44967198, -0.73131523], [ 1.18982913, 0.94670798, 0.82973421], [-1.57680831, -0.99732066, 0.96432 ], [-0.77483149, -1.23802881, 0.44061227], [ 1.77666419, 0.24931983, -1.12960153]])>>> obj = pd.DataFrame(data)>>> obj 0 1 20 -2.162312 0.449672 -0.7313151 1.189829 0.946708 0.8297342 -1.576808 -0.997321 0.9643203 -0.774831 -1.238029 0.4406124 1.776664 0.249320 -1.129602 指定索引(index)和列标签(columns),和 Series 对象类似,可以在构建的时候添加索引和标签,也可以直接通过赋值的方式就地修改: 1234567891011121314151617181920212223>>> import numpy as np>>> import pandas as pd>>> data = np.random.randn(5,3)>>> index = ['a', 'b', 'c', 'd', 'e']>>> columns = ['A', 'B', 'C']>>> obj = pd.DataFrame(data, index, columns)>>> obj A B Ca -1.042909 -0.238236 -1.050308b 0.587079 0.739683 -0.233624c -0.451254 -0.638496 1.708807d -0.620158 -1.875929 -0.432382e -1.093815 0.396965 -0.759479>>>>>> obj.index = ['A1', 'A2', 'A3', 'A4', 'A5']>>> obj.columns = ['B1', 'B2', 'B3']>>> obj B1 B2 B3A1 -1.042909 -0.238236 -1.050308A2 0.587079 0.739683 -0.233624A3 -0.451254 -0.638496 1.708807A4 -0.620158 -1.875929 -0.432382A5 -1.093815 0.396965 -0.759479 【03x02】通过 dict 构建 DataFrame通过 字典(dict) 构建 DataFrame,字典的键(key)会作为列标签(columns),字典的值(value)会作为数据(data),示例如下: 12345678910111213>>> import pandas as pd>>> data = {'city': ['Wuhan', 'Wuhan', 'Wuhan', 'Beijing', 'Beijing', 'Beijing'], 'year': [2017, 2018, 2019, 2017, 2018, 2019], 'people': [10892900, 11081000, 11212000, 21707000, 21542000, 21536000]}>>> obj = pd.DataFrame(data)>>> obj city year people0 Wuhan 2017 108929001 Wuhan 2018 110810002 Wuhan 2019 112120003 Beijing 2017 217070004 Beijing 2018 215420005 Beijing 2019 21536000 如果指定了列序列,则 DataFrame 的列就会按照指定顺序进行排列,如果传入的列在数据中找不到,就会在结果中产生缺失值(NaN): 12345678910111213141516171819202122232425262728>>> import pandas as pd>>> data = {'city': ['Wuhan', 'Wuhan', 'Wuhan', 'Beijing', 'Beijing', 'Beijing'], 'year': [2017, 2018, 2019, 2017, 2018, 2019], 'people': [10892900, 11081000, 11212000, 21707000, 21542000, 21536000]}>>> pd.DataFrame(data) city year people0 Wuhan 2017 108929001 Wuhan 2018 110810002 Wuhan 2019 112120003 Beijing 2017 217070004 Beijing 2018 215420005 Beijing 2019 21536000>>> pd.DataFrame(data, columns=['year', 'city', 'people']) year city people0 2017 Wuhan 108929001 2018 Wuhan 110810002 2019 Wuhan 112120003 2017 Beijing 217070004 2018 Beijing 215420005 2019 Beijing 21536000>>> pd.DataFrame(data, columns=['year', 'city', 'people', 'money']) year city people money0 2017 Wuhan 10892900 NaN1 2018 Wuhan 11081000 NaN2 2019 Wuhan 11212000 NaN3 2017 Beijing 21707000 NaN4 2018 Beijing 21542000 NaN5 2019 Beijing 21536000 NaN 注意:data 为字典,且未设置 columns 参数时: Python > = 3.6 且 Pandas > = 0.23,DataFrame 的列按字典的插入顺序排序。 Python < 3.6 或 Pandas < 0.23,DataFrame 的列按字典键的字母排序。 【03x03】获取其数据和索引和 Series 一样,DataFrame 也可以通过其 values 和 index 属性获取其数据和索引对象: 123456789101112131415>>> import numpy as np>>> import pandas as pd>>> data = {'city': ['Wuhan', 'Wuhan', 'Wuhan', 'Beijing', 'Beijing', 'Beijing'], 'year': [2017, 2018, 2019, 2017, 2018, 2019], 'people': [10892900, 11081000, 11212000, 21707000, 21542000, 21536000]}>>> obj = pd.DataFrame(data)>>> obj.indexRangeIndex(start=0, stop=6, step=1)>>> obj.valuesarray([['Wuhan', 2017, 10892900], ['Wuhan', 2018, 11081000], ['Wuhan', 2019, 11212000], ['Beijing', 2017, 21707000], ['Beijing', 2018, 21542000], ['Beijing', 2019, 21536000]], dtype=object) 【03x04】通过索引获取数据通过类似字典标记的方式或属性的方式,可以将 DataFrame 的列获取为一个 Series 对象; 行也可以通过位置或名称的方式进行获取,比如用 loc 属性; 对于特别大的 DataFrame,有一个 head 方法可以选取前五行数据。 用法示例: 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849>>> import numpy as np>>> import pandas as pd>>> data = {'city': ['Wuhan', 'Wuhan', 'Wuhan', 'Beijing', 'Beijing', 'Beijing'], 'year': [2017, 2018, 2019, 2017, 2018, 2019], 'people': [10892900, 11081000, 11212000, 21707000, 21542000, 21536000]}>>> obj = pd.DataFrame(data)>>> obj city year people0 Wuhan 2017 108929001 Wuhan 2018 110810002 Wuhan 2019 112120003 Beijing 2017 217070004 Beijing 2018 215420005 Beijing 2019 21536000>>>>>> obj['city']0 Wuhan1 Wuhan2 Wuhan3 Beijing4 Beijing5 BeijingName: city, dtype: object>>>>>> obj.year0 20171 20182 20193 20174 20185 2019Name: year, dtype: int64>>>>>> type(obj.year)<class 'pandas.core.series.Series'>>>>>>> obj.loc[2]city Wuhanyear 2019people 11212000Name: 2, dtype: object>>>>>> obj.head() city year people0 Wuhan 2017 108929001 Wuhan 2018 110810002 Wuhan 2019 112120003 Beijing 2017 217070004 Beijing 2018 21542000 【03x05】修改列的值列可以通过赋值的方式进行修改。在下面示例中,分别给”money”列赋上一个标量值和一组值: 1234567891011121314151617181920212223242526272829303132333435>>> import pandas as pd>>> import numpy as np>>> data = {'city': ['Wuhan', 'Wuhan', 'Wuhan', 'Beijing', 'Beijing', 'Beijing'], 'year': [2017, 2018, 2019, 2017, 2018, 2019], 'people': [10892900, 11081000, 11212000, 21707000, 21542000, 21536000], 'money':[np.NaN, np.NaN, np.NaN, np.NaN, np.NaN, np.NaN]}>>> obj = pd.DataFrame(data, index=['A', 'B', 'C', 'D', 'E', 'F'])>>> obj city year people moneyA Wuhan 2017 10892900 NaNB Wuhan 2018 11081000 NaNC Wuhan 2019 11212000 NaND Beijing 2017 21707000 NaNE Beijing 2018 21542000 NaNF Beijing 2019 21536000 NaN>>>>>> obj['money'] = 6666666666>>> obj city year people moneyA Wuhan 2017 10892900 6666666666B Wuhan 2018 11081000 6666666666C Wuhan 2019 11212000 6666666666D Beijing 2017 21707000 6666666666E Beijing 2018 21542000 6666666666F Beijing 2019 21536000 6666666666>>>>>> obj['money'] = np.arange(100000000, 700000000, 100000000)>>> obj city year people moneyA Wuhan 2017 10892900 100000000B Wuhan 2018 11081000 200000000C Wuhan 2019 11212000 300000000D Beijing 2017 21707000 400000000E Beijing 2018 21542000 500000000F Beijing 2019 21536000 600000000 将列表或数组赋值给某个列时,其长度必须跟 DataFrame 的长度相匹配。如果赋值的是一个 Series,就会精确匹配 DataFrame 的索引: 1234567891011121314151617181920212223242526>>> import pandas as pd>>> import numpy as np>>> data = {'city': ['Wuhan', 'Wuhan', 'Wuhan', 'Beijing', 'Beijing', 'Beijing'], 'year': [2017, 2018, 2019, 2017, 2018, 2019], 'people': [10892900, 11081000, 11212000, 21707000, 21542000, 21536000], 'money':[np.NaN, np.NaN, np.NaN, np.NaN, np.NaN, np.NaN]}>>> obj = pd.DataFrame(data, index=['A', 'B', 'C', 'D', 'E', 'F'])>>> obj city year people moneyA Wuhan 2017 10892900 NaNB Wuhan 2018 11081000 NaNC Wuhan 2019 11212000 NaND Beijing 2017 21707000 NaNE Beijing 2018 21542000 NaNF Beijing 2019 21536000 NaN>>> >>> new_data = pd.Series([5670000000, 6890000000, 7890000000], index=['A', 'C', 'E'])>>> obj['money'] = new_data>>> obj city year people moneyA Wuhan 2017 10892900 5.670000e+09B Wuhan 2018 11081000 NaNC Wuhan 2019 11212000 6.890000e+09D Beijing 2017 21707000 NaNE Beijing 2018 21542000 7.890000e+09F Beijing 2019 21536000 NaN 【03x06】增加 / 删除列为不存在的列赋值会创建出一个新列,关键字 del 用于删除列: 123456789101112131415161718192021222324252627282930313233>>> import pandas as pd>>> data = {'city': ['Wuhan', 'Wuhan', 'Wuhan', 'Beijing', 'Beijing', 'Beijing'], 'year': [2017, 2018, 2019, 2017, 2018, 2019], 'people': [10892900, 11081000, 11212000, 21707000, 21542000, 21536000]}>>> obj = pd.DataFrame(data)>>> obj city year people0 Wuhan 2017 108929001 Wuhan 2018 110810002 Wuhan 2019 112120003 Beijing 2017 217070004 Beijing 2018 215420005 Beijing 2019 21536000>>> >>> obj['northern'] = obj['city'] == 'Beijing'>>> obj city year people northern0 Wuhan 2017 10892900 False1 Wuhan 2018 11081000 False2 Wuhan 2019 11212000 False3 Beijing 2017 21707000 True4 Beijing 2018 21542000 True5 Beijing 2019 21536000 True>>> >>> del obj['northern']>>> obj city year people0 Wuhan 2017 108929001 Wuhan 2018 110810002 Wuhan 2019 112120003 Beijing 2017 217070004 Beijing 2018 215420005 Beijing 2019 21536000 【03x07】name 属性可以通过 index.name 和 columns.name 属性设置索引(index)和列标签(columns)的 name,注意 DataFrame 对象是没有 name 属性的: 12345678910111213141516>>> import pandas as pd>>> data = {'city': ['Wuhan', 'Wuhan', 'Wuhan', 'Beijing', 'Beijing', 'Beijing'], 'year': [2017, 2018, 2019, 2017, 2018, 2019], 'people': [10892900, 11081000, 11212000, 21707000, 21542000, 21536000]}>>> obj = pd.DataFrame(data)>>> obj.index.name = 'index'>>> obj.columns.name = 'columns'>>> objcolumns city year peopleindex 0 Wuhan 2017 108929001 Wuhan 2018 110810002 Wuhan 2019 112120003 Beijing 2017 217070004 Beijing 2018 215420005 Beijing 2019 21536000 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106676693未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Pandas","slug":"Python-数据分析/Pandas","permalink":"https://www.itrhx.com/categories/Python-数据分析/Pandas/"}],"tags":[{"name":"Pandas","slug":"Pandas","permalink":"https://www.itrhx.com/tags/Pandas/"},{"name":"Series","slug":"Series","permalink":"https://www.itrhx.com/tags/Series/"},{"name":"DataFrame","slug":"DataFrame","permalink":"https://www.itrhx.com/tags/DataFrame/"}]},{"title":"Python 数据分析三剑客之 Matplotlib(十一):最常用最有价值的 50 个图表【译文】","slug":"A78-Matplotlib-11","date":"2020-06-09T08:13:44.322Z","updated":"2020-08-06T03:30:09.138Z","comments":true,"path":"2020/06/09/A78-Matplotlib-11/","link":"","permalink":"https://www.itrhx.com/2020/06/09/A78-Matplotlib-11/","excerpt":"","text":"Matplotlib 系列文章: Python 数据分析三剑客之 Matplotlib(一):初识 Matplotlib 与其 matplotibrc 配置文件 Python 数据分析三剑客之 Matplotlib(二):文本描述 / 中文支持 / 画布 / 网格等基本图像属性 Python 数据分析三剑客之 Matplotlib(三):图例 / LaTeX / 刻度 / 子图 / 补丁等基本图像属性 Python 数据分析三剑客之 Matplotlib(四):线性图的绘制 Python 数据分析三剑客之 Matplotlib(五):散点图的绘制 Python 数据分析三剑客之 Matplotlib(六):直方图 / 柱状图 / 条形图的绘制 Python 数据分析三剑客之 Matplotlib(七):饼状图的绘制 Python 数据分析三剑客之 Matplotlib(八):等高线 / 等值线图的绘制 Python 数据分析三剑客之 Matplotlib(九):极区图 / 极坐标图 / 雷达图的绘制 Python 数据分析三剑客之 Matplotlib(十):3D 图的绘制 Python 数据分析三剑客之 Matplotlib(十一):最热门最常用的 50 个图表【译文】 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 翻译丨TRHX作者丨Selva Prabhakaran原文丨《Top 50 matplotlib Visualizations – The Master Plots (with full python code)》 ★ 本文中的示例原作者使用的编辑器为 Jupyter Notebook;★ 译者使用 PyCharm 测试原文中有部分代码不太准确,部分已进行修改,对应有注释说明;★ 运行本文代码,需要安装 Matplotlib 和 Seaborn 等可视化库,其他的一些辅助可视化库已在代码部分作标注;★ 示例中用到的数据均储存在作者的 GitHub:https://github.com/selva86/datasets,因此运行程序可能需要FQ;★ 译者英文水平有限,若遇到翻译模糊的词建议参考原文来理解。★ 本文50个示例代码已打包为 .py 文件,可直接下载:https://download.csdn.net/download/qq_36759224/12507219 1234这里是一段防爬虫文本,请读者忽略。本译文首发于 CSDN,作者 Selva Prabhakaran,译者 TRHX。本文链接:https://itrhx.blog.csdn.net/article/details/106558131原文链接:https://www.machinelearningplus.com/plots/top-50-matplotlib-visualizations-the-master-plots-python/ 【1x00】介绍(Introduction)在数据分析和可视化中最常用的、最有价值的前 50 个 Matplotlib 图表。这些图表会让你懂得在不同情况下合理使用 Python 的 Matplotlib 和 Seaborn 库来达到数据可视化效果。 这些图表根据可视化目标的 7 个不同情景进行分组。 例如,如果要想象两个变量之间的关系,请查看“关联”部分下的图表。 或者,如果您想要显示值如何随时间变化,请查看“变化”部分,依此类推。 有效图表的重要特征: 在不歪曲事实的情况下传达正确和必要的信息; 设计简单,不必太费力就能理解它; 从审美角度支持信息而不是掩盖信息; 信息没有超负荷。 【2x00】准备工作(Setup)在代码运行前先引入下面的基本设置,当然,个别图表可能会重新定义显示要素。 12345678910111213141516171819202122232425# !pip install brewer2mplimport numpy as npimport pandas as pdimport matplotlib as mplimport matplotlib.pyplot as pltimport seaborn as snsimport warnings; warnings.filterwarnings(action='once')large = 22; med = 16; small = 12params = {'axes.titlesize': large, 'legend.fontsize': med, 'figure.figsize': (16, 10), 'axes.labelsize': med, 'axes.titlesize': med, 'xtick.labelsize': med, 'ytick.labelsize': med, 'figure.titlesize': large}plt.rcParams.update(params)plt.style.use('seaborn-whitegrid')sns.set_style(\"white\")%matplotlib inline# Versionprint(mpl.__version__) #> 3.0.0print(sns.__version__) #> 0.9.0 【3x00】关联(Correlation) 关联图用于可视化两个或多个变量之间的关系。也就是说,一个变量相对于另一个变量如何变化。 【01】散点图(Scatter plot)散点图是研究两个变量之间关系的经典和基本的绘图。如果数据中有多个组,则可能需要以不同的颜色显示每个组。在 Matplotlib 中,您可以使用 plt.scatterplot() 方便地执行此操作。 12345678910111213141516171819202122232425# Import dataset midwest = pd.read_csv(\"https://raw.githubusercontent.com/selva86/datasets/master/midwest_filter.csv\")# Prepare Data # Create as many colors as there are unique midwest['category']categories = np.unique(midwest['category'])colors = [plt.cm.tab10(i/float(len(categories)-1)) for i in range(len(categories))]# Draw Plot for Each Categoryplt.figure(figsize=(16, 10), dpi= 80, facecolor='w', edgecolor='k')for i, category in enumerate(categories): plt.scatter('area', 'poptotal', data=midwest.loc[midwest.category==category, :], s=20, cmap=colors[i], label=str(category))# 原文 c=colors[i] 已修改为 cmap=colors[i]# Decorationsplt.gca().set(xlim=(0.0, 0.1), ylim=(0, 90000), xlabel='Area', ylabel='Population')plt.xticks(fontsize=12); plt.yticks(fontsize=12)plt.title(\"Scatterplot of Midwest Area vs Population\", fontsize=22)plt.legend(fontsize=12)plt.show() 【02】带边界的气泡图(Bubble plot with Encircling)有时候您想在一个边界内显示一组点来强调它们的重要性。在本例中,您将从被包围的数据中获取记录,并将其传递给下面的代码中描述的 encircle()。 12345678910111213141516171819202122232425262728293031323334353637383940414243from matplotlib import patchesfrom scipy.spatial import ConvexHullimport warnings; warnings.simplefilter('ignore')sns.set_style(\"white\")# Step 1: Prepare Datamidwest = pd.read_csv(\"https://raw.githubusercontent.com/selva86/datasets/master/midwest_filter.csv\")# As many colors as there are unique midwest['category']categories = np.unique(midwest['category'])colors = [plt.cm.tab10(i/float(len(categories)-1)) for i in range(len(categories))]# Step 2: Draw Scatterplot with unique color for each categoryfig = plt.figure(figsize=(16, 10), dpi=80, facecolor='w', edgecolor='k')for i, category in enumerate(categories): plt.scatter('area', 'poptotal', data=midwest.loc[midwest.category == category, :], s='dot_size', cmap=colors[i], label=str(category), edgecolors='black', linewidths=.5)# 原文 c=colors[i] 已修改为 cmap=colors[i]# Step 3: Encircling# https://stackoverflow.com/questions/44575681/how-do-i-encircle-different-data-sets-in-scatter-plotdef encircle(x,y, ax=None, **kw): if not ax: ax = plt.gca() p = np.c_[x, y] hull = ConvexHull(p) poly = plt.Polygon(p[hull.vertices, :], **kw) ax.add_patch(poly)# Select data to be encircledmidwest_encircle_data = midwest.loc[midwest.state=='IN', :]# Draw polygon surrounding verticesencircle(midwest_encircle_data.area, midwest_encircle_data.poptotal, ec=\"k\", fc=\"gold\", alpha=0.1)encircle(midwest_encircle_data.area, midwest_encircle_data.poptotal, ec=\"firebrick\", fc=\"none\", linewidth=1.5)# Step 4: Decorationsplt.gca().set(xlim=(0.0, 0.1), ylim=(0, 90000), xlabel='Area', ylabel='Population')plt.xticks(fontsize=12); plt.yticks(fontsize=12)plt.title(\"Bubble Plot with Encircling\", fontsize=22)plt.legend(fontsize=12)plt.show() 【03】带线性回归最佳拟合线的散点图(Scatter plot with linear regression line of best fit)如果你想了解两个变量之间是如何变化的,那么最佳拟合线就是常用的方法。下图显示了数据中不同组之间的最佳拟合线的差异。若要禁用分组并只为整个数据集绘制一条最佳拟合线,请从 sns.lmplot() 方法中删除 hue ='cyl' 参数。 1234567891011121314# Import Datadf = pd.read_csv(\"https://raw.githubusercontent.com/selva86/datasets/master/mpg_ggplot2.csv\")df_select = df.loc[df.cyl.isin([4, 8]), :]# Plotsns.set_style(\"white\")gridobj = sns.lmplot(x=\"displ\", y=\"hwy\", hue=\"cyl\", data=df_select, height=7, aspect=1.6, robust=True, palette='tab10', scatter_kws=dict(s=60, linewidths=.7, edgecolors='black'))# Decorationsgridobj.set(xlim=(0.5, 7.5), ylim=(0, 50))plt.title(\"Scatterplot with line of best fit grouped by number of cylinders\", fontsize=20)plt.show() 针对每一组数据绘制线性回归线(Each regression line in its own column),可以通过在 sns.lmplot() 中设置 col=groupingcolumn 参数来实现,如下: 1234567891011121314151617# Import Datadf = pd.read_csv(\"https://raw.githubusercontent.com/selva86/datasets/master/mpg_ggplot2.csv\")df_select = df.loc[df.cyl.isin([4, 8]), :]# Each line in its own columnsns.set_style(\"white\")gridobj = sns.lmplot(x=\"displ\", y=\"hwy\", data=df_select, height=7, robust=True, palette='Set1', col=\"cyl\", scatter_kws=dict(s=60, linewidths=.7, edgecolors='black'))# Decorationsgridobj.set(xlim=(0.5, 7.5), ylim=(0, 50))plt.show() 【04】抖动图(Jittering with stripplot)通常,多个数据点具有完全相同的 X 和 Y 值。 此时多个点绘制会重叠并隐藏。为避免这种情况,可以将数据点稍微抖动,以便可以直观地看到它们。 使用 seaborn 库的 stripplot() 方法可以很方便的实现这个功能。 12345678910# Import Datadf = pd.read_csv(\"https://raw.githubusercontent.com/selva86/datasets/master/mpg_ggplot2.csv\")# Draw Stripplotfig, ax = plt.subplots(figsize=(16,10), dpi= 80)sns.stripplot(df.cty, df.hwy, jitter=0.25, size=8, ax=ax, linewidth=.5)# Decorationsplt.title('Use jittered plots to avoid overlapping of points', fontsize=22)plt.show() 【05】计数图(Counts Plot)避免点重叠问题的另一个选择是根据点的位置增加点的大小。所以,点的大小越大,它周围的点就越集中。 1234567891011# Import Datadf = pd.read_csv(\"https://raw.githubusercontent.com/selva86/datasets/master/mpg_ggplot2.csv\")df_counts = df.groupby(['hwy', 'cty']).size().reset_index(name='counts')# Draw Stripplotfig, ax = plt.subplots(figsize=(16,10), dpi= 80) sns.stripplot(df_counts.cty, df_counts.hwy, size=df_counts.counts*2, ax=ax)# Decorationsplt.title('Counts Plot - Size of circle is bigger as more points overlap', fontsize=22)plt.show() 【06】边缘直方图(Marginal Histogram)边缘直方图是具有沿 X 和 Y 轴变量的直方图。 这用于可视化 X 和 Y 之间的关系以及单独的 X 和 Y 的单变量分布。 这种图经常用于探索性数据分析(EDA)。 12345678910111213141516171819202122232425262728293031# Import Datadf = pd.read_csv(\"https://raw.githubusercontent.com/selva86/datasets/master/mpg_ggplot2.csv\")# Create Fig and gridspecfig = plt.figure(figsize=(16, 10), dpi= 80)grid = plt.GridSpec(4, 4, hspace=0.5, wspace=0.2)# Define the axesax_main = fig.add_subplot(grid[:-1, :-1])ax_right = fig.add_subplot(grid[:-1, -1], xticklabels=[], yticklabels=[])ax_bottom = fig.add_subplot(grid[-1, 0:-1], xticklabels=[], yticklabels=[])# Scatterplot on main axax_main.scatter('displ', 'hwy', s=df.cty*4, c=df.manufacturer.astype('category').cat.codes, alpha=.9, data=df, cmap=\"tab10\", edgecolors='gray', linewidths=.5)# histogram on the rightax_bottom.hist(df.displ, 40, histtype='stepfilled', orientation='vertical', color='deeppink')ax_bottom.invert_yaxis()# histogram in the bottomax_right.hist(df.hwy, 40, histtype='stepfilled', orientation='horizontal', color='deeppink')# Decorationsax_main.set(title='Scatterplot with Histograms \\n displ vs hwy', xlabel='displ', ylabel='hwy')ax_main.title.set_fontsize(20)for item in ([ax_main.xaxis.label, ax_main.yaxis.label] + ax_main.get_xticklabels() + ax_main.get_yticklabels()): item.set_fontsize(14)xlabels = ax_main.get_xticks().tolist()ax_main.set_xticklabels(xlabels)plt.show() 【07】边缘箱形图(Marginal Boxplot)边缘箱形图与边缘直方图具有相似的用途。 然而,箱线图有助于精确定位 X 和 Y 的中位数、第25和第75百分位数。 123456789101112131415161718192021222324252627282930313233# Import Datadf = pd.read_csv(\"https://raw.githubusercontent.com/selva86/datasets/master/mpg_ggplot2.csv\")# Create Fig and gridspecfig = plt.figure(figsize=(16, 10), dpi= 80)grid = plt.GridSpec(4, 4, hspace=0.5, wspace=0.2)# Define the axesax_main = fig.add_subplot(grid[:-1, :-1])ax_right = fig.add_subplot(grid[:-1, -1], xticklabels=[], yticklabels=[])ax_bottom = fig.add_subplot(grid[-1, 0:-1], xticklabels=[], yticklabels=[])# Scatterplot on main axax_main.scatter('displ', 'hwy', s=df.cty*5, c=df.manufacturer.astype('category').cat.codes, alpha=.9, data=df, cmap=\"Set1\", edgecolors='black', linewidths=.5)# Add a graph in each partsns.boxplot(df.hwy, ax=ax_right, orient=\"v\")sns.boxplot(df.displ, ax=ax_bottom, orient=\"h\")# Decorations ------------------# Remove x axis name for the boxplotax_bottom.set(xlabel='')ax_right.set(ylabel='')# Main Title, Xlabel and YLabelax_main.set(title='Scatterplot with Histograms \\n displ vs hwy', xlabel='displ', ylabel='hwy')# Set font size of different componentsax_main.title.set_fontsize(20)for item in ([ax_main.xaxis.label, ax_main.yaxis.label] + ax_main.get_xticklabels() + ax_main.get_yticklabels()): item.set_fontsize(14)plt.show() 【08】相关图(Correllogram)相关图用于直观地查看给定数据帧(或二维数组)中所有可能的数值变量对之间的相关性度量。 123456789101112# Import Datasetdf = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mtcars.csv\")# Plotplt.figure(figsize=(12, 10), dpi=80)sns.heatmap(df.corr(), xticklabels=df.corr().columns, yticklabels=df.corr().columns, cmap='RdYlGn', center=0, annot=True)# Decorationsplt.title('Correlogram of mtcars', fontsize=22)plt.xticks(fontsize=12)plt.yticks(fontsize=12)plt.show() 【09】成对图(Pairwise Plot)成对图是探索性分析中最受欢迎的一种方法,用来理解所有可能的数值变量对之间的关系。它是二元分析的必备工具。 1234567# Load Datasetdf = sns.load_dataset('iris')# Plotplt.figure(figsize=(10, 8), dpi=80)sns.pairplot(df, kind=\"scatter\", hue=\"species\", plot_kws=dict(s=80, edgecolor=\"white\", linewidth=2.5))plt.show() 1234567# Load Datasetdf = sns.load_dataset('iris')# Plotplt.figure(figsize=(10, 8), dpi=80)sns.pairplot(df, kind=\"reg\", hue=\"species\")plt.show() 【4x00】偏差(Deviation)【10】发散型条形图(Diverging Bars)如果您想根据单个指标查看项目的变化情况,并可视化此差异的顺序和数量,那么散型条形图是一个很好的工具。 它有助于快速区分数据组的性能,并且非常直观,并且可以立即传达这一点。 123456789101112131415161718# Prepare Datadf = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mtcars.csv\")x = df.loc[:, ['mpg']]df['mpg_z'] = (x - x.mean())/x.std()df['colors'] = ['red' if x < 0 else 'green' for x in df['mpg_z']]df.sort_values('mpg_z', inplace=True)df.reset_index(inplace=True)# Draw plotplt.figure(figsize=(14,10), dpi= 80)plt.hlines(y=df.index, xmin=0, xmax=df.mpg_z, color=df.colors, alpha=0.4, linewidth=5)# Decorationsplt.gca().set(ylabel='$Model$', xlabel='$Mileage$')plt.yticks(df.index, df.cars, fontsize=12)plt.title('Diverging Bars of Car Mileage', fontdict={'size':20})plt.grid(linestyle='--', alpha=0.5)plt.show() 【11】发散型文本图(Diverging Texts)发散型文本图与发散型条形图相似,如果你希望以一种美观的方式显示图表中每个项目的值,就可以使用这种方法。 123456789101112131415161718192021# Prepare Datadf = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mtcars.csv\")x = df.loc[:, ['mpg']]df['mpg_z'] = (x - x.mean())/x.std()df['colors'] = ['red' if x < 0 else 'green' for x in df['mpg_z']]df.sort_values('mpg_z', inplace=True)df.reset_index(inplace=True)# Draw plotplt.figure(figsize=(14, 14), dpi=80)plt.hlines(y=df.index, xmin=0, xmax=df.mpg_z)for x, y, tex in zip(df.mpg_z, df.index, df.mpg_z): t = plt.text(x, y, round(tex, 2), horizontalalignment='right' if x < 0 else 'left', verticalalignment='center', fontdict={'color':'red' if x < 0 else 'green', 'size':14})# Decorationsplt.yticks(df.index, df.cars, fontsize=12)plt.title('Diverging Text Bars of Car Mileage', fontdict={'size':20})plt.grid(linestyle='--', alpha=0.5)plt.xlim(-2.5, 2.5)plt.show() 【12】发散型散点图(Diverging Dot Plot)发散型散点图类似于发散型条形图。 但是,与发散型条形图相比,没有条形会减少组之间的对比度和差异。 12345678910111213141516171819202122232425262728# Prepare Datadf = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mtcars.csv\")x = df.loc[:, ['mpg']]df['mpg_z'] = (x - x.mean())/x.std()df['colors'] = ['red' if x < 0 else 'darkgreen' for x in df['mpg_z']]df.sort_values('mpg_z', inplace=True)df.reset_index(inplace=True)# Draw plotplt.figure(figsize=(14, 16), dpi=80)plt.scatter(df.mpg_z, df.index, s=450, alpha=.6, color=df.colors)for x, y, tex in zip(df.mpg_z, df.index, df.mpg_z): t = plt.text(x, y, round(tex, 1), horizontalalignment='center', verticalalignment='center', fontdict={'color': 'white'})# Decorations# Lighten bordersplt.gca().spines[\"top\"].set_alpha(.3)plt.gca().spines[\"bottom\"].set_alpha(.3)plt.gca().spines[\"right\"].set_alpha(.3)plt.gca().spines[\"left\"].set_alpha(.3)plt.yticks(df.index, df.cars)plt.title('Diverging Dotplot of Car Mileage', fontdict={'size': 20})plt.xlabel('$Mileage$')plt.grid(linestyle='--', alpha=0.5)plt.xlim(-2.5, 2.5)plt.show() 【13】带标记的发散型棒棒糖图(Diverging Lollipop Chart with Markers)带有标记的棒棒糖提供了一种灵活的方式,强调您想要引起注意的任何重要数据点并在图表中适当地给出推理。 12345678910111213141516171819202122232425262728293031323334353637# Prepare Datadf = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mtcars.csv\")x = df.loc[:, ['mpg']]df['mpg_z'] = (x - x.mean())/x.std()df['colors'] = 'black'# color fiat differentlydf.loc[df.cars == 'Fiat X1-9', 'colors'] = 'darkorange'df.sort_values('mpg_z', inplace=True)df.reset_index(inplace=True)# Draw plotimport matplotlib.patches as patchesplt.figure(figsize=(14, 16), dpi=80)plt.hlines(y=df.index, xmin=0, xmax=df.mpg_z, color=df.colors, alpha=0.4, linewidth=1)plt.scatter(df.mpg_z, df.index, color=df.colors, s=[600 if x == 'Fiat X1-9' else 300 for x in df.cars], alpha=0.6)plt.yticks(df.index, df.cars)plt.xticks(fontsize=12)# Annotateplt.annotate('Mercedes Models', xy=(0.0, 11.0), xytext=(1.0, 11), xycoords='data', fontsize=15, ha='center', va='center', bbox=dict(boxstyle='square', fc='firebrick'), arrowprops=dict(arrowstyle='-[, widthB=2.0, lengthB=1.5', lw=2.0, color='steelblue'), color='white')# Add Patchesp1 = patches.Rectangle((-2.0, -1), width=.3, height=3, alpha=.2, facecolor='red')p2 = patches.Rectangle((1.5, 27), width=.8, height=5, alpha=.2, facecolor='green')plt.gca().add_patch(p1)plt.gca().add_patch(p2)# Decorateplt.title('Diverging Bars of Car Mileage', fontdict={'size': 20})plt.grid(linestyle='--', alpha=0.5)plt.show() 【14】面积图(Area Chart)通过对轴和线之间的区域进行着色,面积图不仅强调波峰和波谷,还强调波峰和波谷的持续时间。 高点持续时间越长,线下面积越大。 1234567891011121314151617181920212223242526272829import numpy as npimport pandas as pd# Prepare Datadf = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/economics.csv\", parse_dates=['date']).head(100)x = np.arange(df.shape[0])y_returns = (df.psavert.diff().fillna(0)/df.psavert.shift(1)).fillna(0) * 100# Plotplt.figure(figsize=(16, 10), dpi=80)plt.fill_between(x[1:], y_returns[1:], 0, where=y_returns[1:] >= 0, facecolor='green', interpolate=True, alpha=0.7)plt.fill_between(x[1:], y_returns[1:], 0, where=y_returns[1:] <= 0, facecolor='red', interpolate=True, alpha=0.7)# Annotateplt.annotate('Peak \\n1975', xy=(94.0, 21.0), xytext=(88.0, 28), bbox=dict(boxstyle='square', fc='firebrick'), arrowprops=dict(facecolor='steelblue', shrink=0.05), fontsize=15, color='white')# Decorationsxtickvals = [str(m)[:3].upper()+\"-\"+str(y) for y, m in zip(df.date.dt.year, df.date.dt.month_name())]plt.gca().set_xticks(x[::6])plt.gca().set_xticklabels(xtickvals[::6], rotation=90, fontdict={'horizontalalignment': 'center', 'verticalalignment': 'center_baseline'})plt.ylim(-35, 35)plt.xlim(1, 100)plt.title(\"Month Economics Return %\", fontsize=22)plt.ylabel('Monthly returns %')plt.grid(alpha=0.5)plt.show() 【5x00】排序(Ranking)【15】有序条形图(Ordered Bar Chart)有序条形图有效地传达了项目的排序顺序。在图表上方添加度量标准的值,用户就可以从图表本身获得精确的信息。 12345678910111213141516171819202122232425262728# Prepare Datadf_raw = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mpg_ggplot2.csv\")df = df_raw[['cty', 'manufacturer']].groupby('manufacturer').apply(lambda x: x.mean())df.sort_values('cty', inplace=True)df.reset_index(inplace=True)# Draw plotimport matplotlib.patches as patchesfig, ax = plt.subplots(figsize=(16,10), facecolor='white', dpi= 80)ax.vlines(x=df.index, ymin=0, ymax=df.cty, color='firebrick', alpha=0.7, linewidth=20)# Annotate Textfor i, cty in enumerate(df.cty): ax.text(i, cty+0.5, round(cty, 1), horizontalalignment='center')# Title, Label, Ticks and Ylimax.set_title('Bar Chart for Highway Mileage', fontdict={'size':22})ax.set(ylabel='Miles Per Gallon', ylim=(0, 30))plt.xticks(df.index, df.manufacturer.str.upper(), rotation=60, horizontalalignment='right', fontsize=12)# Add patches to color the X axis labelsp1 = patches.Rectangle((.57, -0.005), width=.33, height=.13, alpha=.1, facecolor='green', transform=fig.transFigure)p2 = patches.Rectangle((.124, -0.005), width=.446, height=.13, alpha=.1, facecolor='red', transform=fig.transFigure)fig.add_artist(p1)fig.add_artist(p2)plt.show() 【16】棒棒糖图(Lollipop Chart)棒棒糖图表以一种视觉上令人愉悦的方式提供与有序条形图类似的目的。 1234567891011121314151617181920212223# Prepare Datadf_raw = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mpg_ggplot2.csv\")df = df_raw[['cty', 'manufacturer']].groupby('manufacturer').apply(lambda x: x.mean())df.sort_values('cty', inplace=True)df.reset_index(inplace=True)# Draw plotfig, ax = plt.subplots(figsize=(16, 10), dpi=80)ax.vlines(x=df.index, ymin=0, ymax=df.cty, color='firebrick', alpha=0.7, linewidth=2)ax.scatter(x=df.index, y=df.cty, s=75, color='firebrick', alpha=0.7)# Title, Label, Ticks and Ylimax.set_title('Lollipop Chart for Highway Mileage', fontdict={'size': 22})ax.set_ylabel('Miles Per Gallon')ax.set_xticks(df.index)ax.set_xticklabels(df.manufacturer.str.upper(), rotation=60, fontdict={'horizontalalignment': 'right', 'size': 12})ax.set_ylim(0, 30)# Annotatefor row in df.itertuples(): ax.text(row.Index, row.cty+.5, s=round(row.cty, 2), horizontalalignment='center', verticalalignment='bottom', fontsize=14)plt.show() 【17】点图(Dot Plot)点图可以表示项目的排名顺序。由于它是沿水平轴对齐的,所以可以更容易地看到点之间的距离。 123456789101112131415161718# Prepare Datadf_raw = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mpg_ggplot2.csv\")df = df_raw[['cty', 'manufacturer']].groupby('manufacturer').apply(lambda x: x.mean())df.sort_values('cty', inplace=True)df.reset_index(inplace=True)# Draw plotfig, ax = plt.subplots(figsize=(16, 10), dpi=80)ax.hlines(y=df.index, xmin=11, xmax=26, color='gray', alpha=0.7, linewidth=1, linestyles='dashdot')ax.scatter(y=df.index, x=df.cty, s=75, color='firebrick', alpha=0.7)# Title, Label, Ticks and Ylimax.set_title('Dot Plot for Highway Mileage', fontdict={'size': 22})ax.set_xlabel('Miles Per Gallon')ax.set_yticks(df.index)ax.set_yticklabels(df.manufacturer.str.title(), fontdict={'horizontalalignment': 'right'})ax.set_xlim(10, 27)plt.show() 【18】坡度图(Slope Chart)坡度图最适合比较给定人员/项目的“前”和“后”位置。 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657import matplotlib.lines as mlines# Import Datadf = pd.read_csv(\"https://raw.githubusercontent.com/selva86/datasets/master/gdppercap.csv\")left_label = [str(c) + ', ' + str(round(y)) for c, y in zip(df.continent, df['1952'])]right_label = [str(c) + ', ' + str(round(y)) for c, y in zip(df.continent, df['1957'])]klass = ['red' if (y1 - y2) < 0 else 'green' for y1, y2 in zip(df['1952'], df['1957'])]# draw line# https://stackoverflow.com/questions/36470343/how-to-draw-a-line-with-matplotlib/36479941def newline(p1, p2, color='black'): ax = plt.gca() l = mlines.Line2D([p1[0], p2[0]], [p1[1], p2[1]], color='red' if p1[1] - p2[1] > 0 else 'green', marker='o', markersize=6) ax.add_line(l) return lfig, ax = plt.subplots(1, 1, figsize=(14, 14), dpi=80)# Vertical Linesax.vlines(x=1, ymin=500, ymax=13000, color='black', alpha=0.7, linewidth=1, linestyles='dotted')ax.vlines(x=3, ymin=500, ymax=13000, color='black', alpha=0.7, linewidth=1, linestyles='dotted')# Pointsax.scatter(y=df['1952'], x=np.repeat(1, df.shape[0]), s=10, color='black', alpha=0.7)ax.scatter(y=df['1957'], x=np.repeat(3, df.shape[0]), s=10, color='black', alpha=0.7)# Line Segmentsand Annotationfor p1, p2, c in zip(df['1952'], df['1957'], df['continent']): newline([1, p1], [3, p2]) ax.text(1 - 0.05, p1, c + ', ' + str(round(p1)), horizontalalignment='right', verticalalignment='center', fontdict={'size': 14}) ax.text(3 + 0.05, p2, c + ', ' + str(round(p2)), horizontalalignment='left', verticalalignment='center', fontdict={'size': 14})# 'Before' and 'After' Annotationsax.text(1 - 0.05, 13000, 'BEFORE', horizontalalignment='right', verticalalignment='center', fontdict={'size': 18, 'weight': 700})ax.text(3 + 0.05, 13000, 'AFTER', horizontalalignment='left', verticalalignment='center', fontdict={'size': 18, 'weight': 700})# Decorationax.set_title(\"Slopechart: Comparing GDP Per Capita between 1952 vs 1957\", fontdict={'size': 22})ax.set(xlim=(0, 4), ylim=(0, 14000), ylabel='Mean GDP Per Capita')ax.set_xticks([1, 3])ax.set_xticklabels([\"1952\", \"1957\"])plt.yticks(np.arange(500, 13000, 2000), fontsize=12)# Lighten bordersplt.gca().spines[\"top\"].set_alpha(.0)plt.gca().spines[\"bottom\"].set_alpha(.0)plt.gca().spines[\"right\"].set_alpha(.0)plt.gca().spines[\"left\"].set_alpha(.0)plt.show() 【19】哑铃图(Dumbbell Plot)哑铃图传达了各种项目的“前”和“后”位置以及项目的等级顺序。如果您希望可视化特定项目/计划对不同对象的影响,那么它非常有用。 1234567891011121314151617181920212223242526272829303132333435363738394041import matplotlib.lines as mlines# Import Datadf = pd.read_csv(\"https://raw.githubusercontent.com/selva86/datasets/master/health.csv\")df.sort_values('pct_2014', inplace=True)df.reset_index(inplace=True)# Func to draw line segmentdef newline(p1, p2, color='black'): ax = plt.gca() l = mlines.Line2D([p1[0], p2[0]], [p1[1], p2[1]], color='skyblue') ax.add_line(l) return l# Figure and Axesfig, ax = plt.subplots(1, 1, figsize=(14, 14), facecolor='#f7f7f7', dpi=80)# Vertical Linesax.vlines(x=.05, ymin=0, ymax=26, color='black', alpha=1, linewidth=1, linestyles='dotted')ax.vlines(x=.10, ymin=0, ymax=26, color='black', alpha=1, linewidth=1, linestyles='dotted')ax.vlines(x=.15, ymin=0, ymax=26, color='black', alpha=1, linewidth=1, linestyles='dotted')ax.vlines(x=.20, ymin=0, ymax=26, color='black', alpha=1, linewidth=1, linestyles='dotted')# Pointsax.scatter(y=df['index'], x=df['pct_2013'], s=50, color='#0e668b', alpha=0.7)ax.scatter(y=df['index'], x=df['pct_2014'], s=50, color='#a3c4dc', alpha=0.7)# Line Segmentsfor i, p1, p2 in zip(df['index'], df['pct_2013'], df['pct_2014']): newline([p1, i], [p2, i])# Decorationax.set_facecolor('#f7f7f7')ax.set_title(\"Dumbell Chart: Pct Change - 2013 vs 2014\", fontdict={'size': 22})ax.set(xlim=(0, .25), ylim=(-1, 27), ylabel='Mean GDP Per Capita')ax.set_xticks([.05, .1, .15, .20])ax.set_xticklabels(['5%', '15%', '20%', '25%'])ax.set_xticklabels(['5%', '15%', '20%', '25%'])plt.show() 【6x00】分布(Distribution)【20】连续变量的直方图(Histogram for Continuous Variable)连续变量的直方图显示给定变量的频率分布。下面的图表基于分类变量对频率条进行分组,从而更深入地了解连续变量和分类变量。 12345678910111213141516171819202122# Import Datadf = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mpg_ggplot2.csv\")# Prepare datax_var = 'displ'groupby_var = 'class'df_agg = df.loc[:, [x_var, groupby_var]].groupby(groupby_var)vals = [df[x_var].values.tolist() for i, df in df_agg]# Drawplt.figure(figsize=(16, 9), dpi=80)colors = [plt.cm.Spectral(i / float(len(vals) - 1)) for i in range(len(vals))]n, bins, patches = plt.hist(vals, 30, stacked=True, density=False, color=colors[:len(vals)])# Decorationplt.legend({group: col for group, col in zip(np.unique(df[groupby_var]).tolist(), colors[:len(vals)])})plt.title(f\"Stacked Histogram of ${x_var}$ colored by ${groupby_var}$\", fontsize=22)plt.xlabel(x_var)plt.ylabel(\"Frequency\")plt.ylim(0, 25)plt.xticks(ticks=bins[::3], labels=[round(b, 1) for b in bins[::3]])plt.show() 【21】分类变量的直方图(Histogram for Categorical Variable)分类变量的直方图显示该变量的频率分布。通过给条形图上色,您可以将分布与表示颜色的另一个类型变量相关联。 12345678910111213141516171819202122# Import Datadf = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mpg_ggplot2.csv\")# Prepare datax_var = 'manufacturer'groupby_var = 'class'df_agg = df.loc[:, [x_var, groupby_var]].groupby(groupby_var)vals = [df[x_var].values.tolist() for i, df in df_agg]# Drawplt.figure(figsize=(16, 9), dpi=80)colors = [plt.cm.Spectral(i / float(len(vals) - 1)) for i in range(len(vals))]n, bins, patches = plt.hist(vals, df[x_var].unique().__len__(), stacked=True, density=False, color=colors[:len(vals)])# Decorationplt.legend({group: col for group, col in zip(np.unique(df[groupby_var]).tolist(), colors[:len(vals)])})plt.title(f\"Stacked Histogram of ${x_var}$ colored by ${groupby_var}$\", fontsize=22)plt.xlabel(x_var)plt.ylabel(\"Frequency\")plt.ylim(0, 40)plt.xticks(ticks=bins, labels=np.unique(df[x_var]).tolist(), rotation=90, horizontalalignment='left')plt.show() 【22】密度图(Density Plot)密度图是连续变量分布可视化的常用工具。通过按“response”变量对它们进行分组,您可以检查 X 和 Y 之间的关系。如果出于代表性目的来描述城市里程分布如何随气缸数而变化,请参见下面的情况。 1234567891011121314# Import Datadf = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mpg_ggplot2.csv\")# Draw Plotplt.figure(figsize=(16, 10), dpi=80)sns.kdeplot(df.loc[df['cyl'] == 4, \"cty\"], shade=True, color=\"g\", label=\"Cyl=4\", alpha=.7)sns.kdeplot(df.loc[df['cyl'] == 5, \"cty\"], shade=True, color=\"deeppink\", label=\"Cyl=5\", alpha=.7)sns.kdeplot(df.loc[df['cyl'] == 6, \"cty\"], shade=True, color=\"dodgerblue\", label=\"Cyl=6\", alpha=.7)sns.kdeplot(df.loc[df['cyl'] == 8, \"cty\"], shade=True, color=\"orange\", label=\"Cyl=8\", alpha=.7)# Decorationplt.title('Density Plot of City Mileage by n_Cylinders', fontsize=22)plt.legend()plt.show() 【23】直方图密度曲线(Density Curves with Histogram)具有直方图的密度曲线将两个图所传达的信息集合在一起,因此您可以将它们都放在一个图形中,而不是放在两个图形中。 1234567891011121314151617# Import Datadf = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mpg_ggplot2.csv\")# Draw Plotplt.figure(figsize=(13, 10), dpi=80)sns.distplot(df.loc[df['class'] == 'compact', \"cty\"], color=\"dodgerblue\", label=\"Compact\", hist_kws={'alpha': .7}, kde_kws={'linewidth': 3})sns.distplot(df.loc[df['class'] == 'suv', \"cty\"], color=\"orange\", label=\"SUV\", hist_kws={'alpha': .7}, kde_kws={'linewidth': 3})sns.distplot(df.loc[df['class'] == 'minivan', \"cty\"], color=\"g\", label=\"minivan\", hist_kws={'alpha': .7}, kde_kws={'linewidth': 3})plt.ylim(0, 0.35)# Decorationplt.title('Density Plot of City Mileage by Vehicle Type', fontsize=22)plt.legend()plt.show() 【24】山峰叠峦图 / 欢乐图(Joy Plot)Joy Plot 允许不同组的密度曲线重叠,这是一种很好的可视化方法,可以直观地显示大量分组之间的关系。它看起来赏心悦目,清楚地传达了正确的信息。它可以使用基于 matplotlib 的 joypy 包轻松构建。 【译者 TRHX 注:Joy Plot 看起来就像是山峰叠峦,山峦起伏,层次分明,但取名为 Joy,欢乐的意思,所以不太好翻译,在使用该方法时要先安装 joypy 库】 1234567891011121314# !pip install joypy# Import Dataimport joypy# 原文没有 import joypy,译者 TRHX 添加mpg = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mpg_ggplot2.csv\")# Draw Plotplt.figure(figsize=(16, 10), dpi=80)fig, axes = joypy.joyplot(mpg, column=['hwy', 'cty'], by=\"class\", ylim='own', figsize=(14, 10))# Decorationplt.title('Joy Plot of City and Highway Mileage by Class', fontsize=22)plt.show() 【25】分布式点图(Distributed Dot Plot)分布点图显示按组分割的点的单变量分布。点越暗,数据点在该区域的集中程度就越高。通过对中值进行不同的着色,这些组的真实位置立即变得明显。 1234567891011121314151617181920212223242526272829303132333435363738394041import matplotlib.patches as mpatches# Prepare Datadf_raw = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mpg_ggplot2.csv\")cyl_colors = {4: 'tab:red', 5: 'tab:green', 6: 'tab:blue', 8: 'tab:orange'}df_raw['cyl_color'] = df_raw.cyl.map(cyl_colors)# Mean and Median city mileage by makedf = df_raw[['cty', 'manufacturer']].groupby('manufacturer').apply(lambda x: x.mean())df.sort_values('cty', ascending=False, inplace=True)df.reset_index(inplace=True)df_median = df_raw[['cty', 'manufacturer']].groupby('manufacturer').apply(lambda x: x.median())# Draw horizontal linesfig, ax = plt.subplots(figsize=(16, 10), dpi=80)ax.hlines(y=df.index, xmin=0, xmax=40, color='gray', alpha=0.5, linewidth=.5, linestyles='dashdot')# Draw the Dotsfor i, make in enumerate(df.manufacturer): df_make = df_raw.loc[df_raw.manufacturer == make, :] ax.scatter(y=np.repeat(i, df_make.shape[0]), x='cty', data=df_make, s=75, edgecolors='gray', c='w', alpha=0.5) ax.scatter(y=i, x='cty', data=df_median.loc[df_median.index == make, :], s=75, c='firebrick')# Annotateax.text(33, 13, \"$red \\; dots \\; are \\; the \\: median$\", fontdict={'size': 12}, color='firebrick')# Decorationsred_patch = plt.plot([], [], marker=\"o\", ms=10, ls=\"\", mec=None, color='firebrick', label=\"Median\")plt.legend(handles=red_patch)ax.set_title('Distribution of City Mileage by Make', fontdict={'size': 22})ax.set_xlabel('Miles Per Gallon (City)', alpha=0.7)ax.set_yticks(df.index)ax.set_yticklabels(df.manufacturer.str.title(), fontdict={'horizontalalignment': 'right'}, alpha=0.7)ax.set_xlim(1, 40)plt.xticks(alpha=0.7)plt.gca().spines[\"top\"].set_visible(False)plt.gca().spines[\"bottom\"].set_visible(False)plt.gca().spines[\"right\"].set_visible(False)plt.gca().spines[\"left\"].set_visible(False)plt.grid(axis='both', alpha=.4, linewidth=.1)plt.show() 【26】箱形图(Box Plot)箱形图是可视化分布的一种好方法,同时牢记中位数,第 25 个第 75 个四分位数和离群值。 但是,在解释方框的大小时需要小心,这可能会扭曲该组中包含的点数。 因此,手动提供每个框中的观察次数可以帮助克服此缺点。 例如,左侧的前两个框,尽管它们分别具有 5 和 47 个 obs,但是却具有相同大小, 因此,有必要写下该组中的观察数。 123456789101112131415161718192021222324# Import Datadf = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mpg_ggplot2.csv\")# Draw Plotplt.figure(figsize=(13, 10), dpi=80)sns.boxplot(x='class', y='hwy', data=df, notch=False)# Add N Obs inside boxplot (optional)def add_n_obs(df, group_col, y): medians_dict = {grp[0]: grp[1][y].median() for grp in df.groupby(group_col)} xticklabels = [x.get_text() for x in plt.gca().get_xticklabels()] n_obs = df.groupby(group_col)[y].size().values for (x, xticklabel), n_ob in zip(enumerate(xticklabels), n_obs): plt.text(x, medians_dict[xticklabel] * 1.01, \"#obs : \" + str(n_ob), horizontalalignment='center', fontdict={'size': 14}, color='white')add_n_obs(df, group_col='class', y='hwy')# Decorationplt.title('Box Plot of Highway Mileage by Vehicle Class', fontsize=22)plt.ylim(10, 40)plt.show() 【27】点 + 箱形图(Dot + Box Plot)点 + 箱形图传达类似于分组的箱形图信息。此外,这些点还提供了每组中有多少数据点的含义。 123456789101112131415# Import Datadf = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mpg_ggplot2.csv\")# Draw Plotplt.figure(figsize=(13,10), dpi= 80)sns.boxplot(x='class', y='hwy', data=df, hue='cyl')sns.stripplot(x='class', y='hwy', data=df, color='black', size=3, jitter=1)for i in range(len(df['class'].unique())-1): plt.vlines(i+.5, 10, 45, linestyles='solid', colors='gray', alpha=0.2)# Decorationplt.title('Box Plot of Highway Mileage by Vehicle Class', fontsize=22)plt.legend(title='Cylinders')plt.show() 【28】小提琴图(Violin Plot)小提琴图是箱形图在视觉上令人愉悦的替代品。 小提琴的形状或面积取决于它所持有的观察次数。 但是,小提琴图可能更难以阅读,并且在专业设置中不常用。 12345678910# Import Datadf = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mpg_ggplot2.csv\")# Draw Plotplt.figure(figsize=(13, 10), dpi=80)sns.violinplot(x='class', y='hwy', data=df, scale='width', inner='quartile')# Decorationplt.title('Violin Plot of Highway Mileage by Vehicle Class', fontsize=22)plt.show() 【29】人口金字塔图(Population Pyramid)人口金字塔可用于显示按体积排序的组的分布。或者它也可以用于显示人口的逐级过滤,因为它是用来显示有多少人通过一个营销漏斗(Marketing Funnel)的每个阶段。 12345678910111213141516171819# Read datadf = pd.read_csv(\"https://raw.githubusercontent.com/selva86/datasets/master/email_campaign_funnel.csv\")# Draw Plotplt.figure(figsize=(13, 10), dpi=80)group_col = 'Gender'order_of_bars = df.Stage.unique()[::-1]colors = [plt.cm.Spectral(i / float(len(df[group_col].unique()) - 1)) for i in range(len(df[group_col].unique()))]for c, group in zip(colors, df[group_col].unique()): sns.barplot(x='Users', y='Stage', data=df.loc[df[group_col] == group, :], order=order_of_bars, color=c, label=group)# Decorationsplt.xlabel(\"$Users$\")plt.ylabel(\"Stage of Purchase\")plt.yticks(fontsize=12)plt.title(\"Population Pyramid of the Marketing Funnel\", fontsize=22)plt.legend()plt.show() 【30】分类图(Categorical Plots)由 seaborn 库提供的分类图可用于可视化彼此相关的两个或更多分类变量的计数分布。 123456789101112# Load Datasettitanic = sns.load_dataset(\"titanic\")# Plotg = sns.catplot(\"alive\", col=\"deck\", col_wrap=4, data=titanic[titanic.deck.notnull()], kind=\"count\", height=3.5, aspect=.8, palette='tab20')# 译者 TRHX 注释掉了这一行代码# fig.suptitle('sf')plt.show() 123456789101112# Load Datasettitanic = sns.load_dataset(\"titanic\")# Plotsns.catplot(x=\"age\", y=\"embark_town\", hue=\"sex\", col=\"class\", data=titanic[titanic.embark_town.notnull()], orient=\"h\", height=5, aspect=1, palette=\"tab10\", kind=\"violin\", dodge=True, cut=0, bw=.2)# 译者 TRHX 添加了这行代码plt.show() 【7x00】组成(Composition)【31】华夫饼图(Waffle Chart)华夫饼图可以使用 pywaffle 包创建,用于显示较大群体中的组的组成。 【译者 TRHX 注:在使用该方法时要先安装 pywaffle 库】 123456789101112131415161718192021222324252627282930# ! pip install pywaffle# Reference: https://stackoverflow.com/questions/41400136/how-to-do-waffle-charts-in-python-square-piechartfrom pywaffle import Waffle# Importdf_raw = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mpg_ggplot2.csv\")# Prepare Datadf = df_raw.groupby('class').size().reset_index(name='counts')n_categories = df.shape[0]colors = [plt.cm.inferno_r(i / float(n_categories)) for i in range(n_categories)]# Draw Plot and Decoratefig = plt.figure( FigureClass=Waffle, plots={ '111': { 'values': df['counts'], 'labels': [\"{0} ({1})\".format(n[0], n[1]) for n in df[['class', 'counts']].itertuples()], 'legend': {'loc': 'upper left', 'bbox_to_anchor': (1.05, 1), 'fontsize': 12}, 'title': {'label': '# Vehicles by Class', 'loc': 'center', 'fontsize': 18} }, }, rows=7, colors=colors, figsize=(16, 9))# 译者 TRHX 添加了这行代码plt.show() 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455# ! pip install pywafflefrom pywaffle import Waffle# Import# 译者 TRHX 取消注释了这行代码df_raw = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mpg_ggplot2.csv\")# Prepare Data# By Class Datadf_class = df_raw.groupby('class').size().reset_index(name='counts_class')n_categories = df_class.shape[0]colors_class = [plt.cm.Set3(i / float(n_categories)) for i in range(n_categories)]# By Cylinders Datadf_cyl = df_raw.groupby('cyl').size().reset_index(name='counts_cyl')n_categories = df_cyl.shape[0]colors_cyl = [plt.cm.Spectral(i / float(n_categories)) for i in range(n_categories)]# By Make Datadf_make = df_raw.groupby('manufacturer').size().reset_index(name='counts_make')n_categories = df_make.shape[0]colors_make = [plt.cm.tab20b(i / float(n_categories)) for i in range(n_categories)]# Draw Plot and Decoratefig = plt.figure( FigureClass=Waffle, plots={ '311': { 'values': df_class['counts_class'], 'labels': [\"{1}\".format(n[0], n[1]) for n in df_class[['class', 'counts_class']].itertuples()], 'legend': {'loc': 'upper left', 'bbox_to_anchor': (1.05, 1), 'fontsize': 12, 'title': 'Class'}, 'title': {'label': '# Vehicles by Class', 'loc': 'center', 'fontsize': 18}, 'colors': colors_class }, '312': { 'values': df_cyl['counts_cyl'], 'labels': [\"{1}\".format(n[0], n[1]) for n in df_cyl[['cyl', 'counts_cyl']].itertuples()], 'legend': {'loc': 'upper left', 'bbox_to_anchor': (1.05, 1), 'fontsize': 12, 'title': 'Cyl'}, 'title': {'label': '# Vehicles by Cyl', 'loc': 'center', 'fontsize': 18}, 'colors': colors_cyl }, '313': { 'values': df_make['counts_make'], 'labels': [\"{1}\".format(n[0], n[1]) for n in df_make[['manufacturer', 'counts_make']].itertuples()], 'legend': {'loc': 'upper left', 'bbox_to_anchor': (1.05, 1), 'fontsize': 12, 'title': 'Manufacturer'}, 'title': {'label': '# Vehicles by Make', 'loc': 'center', 'fontsize': 18}, 'colors': colors_make } }, rows=9, figsize=(16, 14))# 译者 TRHX 添加了这行代码plt.show() 【32】饼图(Pie Chart)饼图是显示组成的经典方法。然而,现在一般不宜使用,因为馅饼部分的面积有时会产生误导。因此,如果要使用饼图,强烈建议您显式地记下饼图每个部分的百分比或数字。 123456789101112131415# Importdf_raw = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mpg_ggplot2.csv\")# Prepare Datadf = df_raw.groupby('class').size()# Make the plot with pandas'''原代码:df.plot(kind='pie', subplots=True, figsize=(8, 8), dpi=80)译者 TRHX 删除了 dpi= 80'''df.plot(kind='pie', subplots=True, figsize=(8, 8))plt.title(\"Pie Chart of Vehicle Class - Bad\")plt.ylabel(\"\")plt.show() 12345678910111213141516171819202122232425262728293031# Importdf_raw = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mpg_ggplot2.csv\")# Prepare Datadf = df_raw.groupby('class').size().reset_index(name='counts')# Draw Plotfig, ax = plt.subplots(figsize=(12, 7), subplot_kw=dict(aspect=\"equal\"), dpi=80)data = df['counts']categories = df['class']explode = [0, 0, 0, 0, 0, 0.1, 0]def func(pct, allvals): absolute = int(pct / 100. * np.sum(allvals)) return \"{:.1f}% ({:d} )\".format(pct, absolute)wedges, texts, autotexts = ax.pie(data, autopct=lambda pct: func(pct, data), textprops=dict(color=\"w\"), colors=plt.cm.Dark2.colors, startangle=140, explode=explode)# Decorationax.legend(wedges, categories, title=\"Vehicle Class\", loc=\"center left\", bbox_to_anchor=(1, 0, 0.5, 1))plt.setp(autotexts, size=10, weight=700)ax.set_title(\"Class of Vehicles: Pie Chart\")plt.show() 【33】矩阵树形图(Treemap)矩阵树形图类似于饼图,它可以更好地完成工作而不会误导每个组的贡献。 【译者 TRHX 注:在使用该方法时要先安装 squarify 库】 1234567891011121314151617181920# pip install squarifyimport squarify# Import Datadf_raw = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mpg_ggplot2.csv\")# Prepare Datadf = df_raw.groupby('class').size().reset_index(name='counts')labels = df.apply(lambda x: str(x[0]) + \"\\n (\" + str(x[1]) + \")\", axis=1)sizes = df['counts'].values.tolist()colors = [plt.cm.Spectral(i / float(len(labels))) for i in range(len(labels))]# Draw Plotplt.figure(figsize=(12, 8), dpi=80)squarify.plot(sizes=sizes, label=labels, color=colors, alpha=.8)# Decorateplt.title('Treemap of Vechile Class')plt.axis('off')plt.show() 【34】条形图(Bar Chart)条形图是一种基于计数或任何给定度量的可视化项的经典方法。在下面的图表中,我为每个项目使用了不同的颜色,但您通常可能希望为所有项目选择一种颜色,除非您按组对它们进行着色。颜色名称存储在下面代码中的 all_colors 中。您可以通过在 plt.plot() 中设置 color 参数来更改条形的颜色。 123456789101112131415161718192021222324import random# Import Datadf_raw = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mpg_ggplot2.csv\")# Prepare Datadf = df_raw.groupby('manufacturer').size().reset_index(name='counts')n = df['manufacturer'].unique().__len__()+1all_colors = list(plt.cm.colors.cnames.keys())random.seed(100)c = random.choices(all_colors, k=n)# Plot Barsplt.figure(figsize=(16,10), dpi= 80)plt.bar(df['manufacturer'], df['counts'], color=c, width=.5)for i, val in enumerate(df['counts'].values): plt.text(i, val, float(val), horizontalalignment='center', verticalalignment='bottom', fontdict={'fontweight':500, 'size':12})# Decorationplt.gca().set_xticklabels(df['manufacturer'], rotation=60, horizontalalignment= 'right')plt.title(\"Number of Vehicles by Manaufacturers\", fontsize=22)plt.ylabel('# Vehicles')plt.ylim(0, 45)plt.show() 【8x00】变化(Change)【35】时间序列图(Time Series Plot)时间序列图用于可视化给定指标随时间的变化。在这里你可以看到 1949 年到 1969 年间的航空客运量是如何变化的。 12345678910111213141516171819202122# Import Datadf = pd.read_csv('https://github.com/selva86/datasets/raw/master/AirPassengers.csv')# Draw Plotplt.figure(figsize=(16, 10), dpi=80)plt.plot('date', 'traffic', data=df, color='tab:red')# Decorationplt.ylim(50, 750)xtick_location = df.index.tolist()[::12]xtick_labels = [x[-4:] for x in df.date.tolist()[::12]]plt.xticks(ticks=xtick_location, labels=xtick_labels, rotation=0, fontsize=12, horizontalalignment='center', alpha=.7)plt.yticks(fontsize=12, alpha=.7)plt.title(\"Air Passengers Traffic (1949 - 1969)\", fontsize=22)plt.grid(axis='both', alpha=.3)# Remove bordersplt.gca().spines[\"top\"].set_alpha(0.0)plt.gca().spines[\"bottom\"].set_alpha(0.3)plt.gca().spines[\"right\"].set_alpha(0.0)plt.gca().spines[\"left\"].set_alpha(0.3)plt.show() 【36】带波峰和波谷注释的时间序列图(Time Series with Peaks and Troughs Annotated)下面的时间序列绘制了所有的波峰和波谷,并注释了所选特殊事件的发生。 1234567891011121314151617181920212223242526272829303132333435363738394041# Import Datadf = pd.read_csv('https://github.com/selva86/datasets/raw/master/AirPassengers.csv')# Get the Peaks and Troughsdata = df['traffic'].valuesdoublediff = np.diff(np.sign(np.diff(data)))peak_locations = np.where(doublediff == -2)[0] + 1doublediff2 = np.diff(np.sign(np.diff(-1 * data)))trough_locations = np.where(doublediff2 == -2)[0] + 1# Draw Plotplt.figure(figsize=(16, 10), dpi=80)plt.plot('date', 'traffic', data=df, color='tab:blue', label='Air Traffic')plt.scatter(df.date[peak_locations], df.traffic[peak_locations], marker=mpl.markers.CARETUPBASE, color='tab:green', s=100, label='Peaks')plt.scatter(df.date[trough_locations], df.traffic[trough_locations], marker=mpl.markers.CARETDOWNBASE, color='tab:red', s=100, label='Troughs')# Annotatefor t, p in zip(trough_locations[1::5], peak_locations[::3]): plt.text(df.date[p], df.traffic[p] + 15, df.date[p], horizontalalignment='center', color='darkgreen') plt.text(df.date[t], df.traffic[t] - 35, df.date[t], horizontalalignment='center', color='darkred')# Decorationplt.ylim(50, 750)xtick_location = df.index.tolist()[::6]xtick_labels = df.date.tolist()[::6]plt.xticks(ticks=xtick_location, labels=xtick_labels, rotation=90, fontsize=12, alpha=.7)plt.title(\"Peak and Troughs of Air Passengers Traffic (1949 - 1969)\", fontsize=22)plt.yticks(fontsize=12, alpha=.7)# Lighten bordersplt.gca().spines[\"top\"].set_alpha(.0)plt.gca().spines[\"bottom\"].set_alpha(.3)plt.gca().spines[\"right\"].set_alpha(.0)plt.gca().spines[\"left\"].set_alpha(.3)plt.legend(loc='upper left')plt.grid(axis='y', alpha=.3)plt.show() 【37】自相关 (ACF) 和部分自相关 (PACF) 图(Autocorrelation (ACF) and Partial Autocorrelation (PACF) Plot)自相关图(ACF图)显示了时间序列与其自身滞后的相关性。 每条垂直线(在自相关图上)表示系列与滞后 0 之间的滞后的相关性。图中的蓝色阴影区域是显著性级别。 那些位于蓝线之上的滞后是显著的滞后。 那么如何解释呢? 对于航空乘客来说,我们看到超过 14 个滞后已经越过蓝线,因此意义重大。这意味着,14 年前的航空客运量对今天的交通量产生了影响。 另一方面,部分自相关图(PACF)显示了任何给定滞后(时间序列)相对于当前序列的自相关,但消除了中间滞后的贡献。 123456789101112131415161718192021from statsmodels.graphics.tsaplots import plot_acf, plot_pacf# Import Datadf = pd.read_csv('https://github.com/selva86/datasets/raw/master/AirPassengers.csv')# Draw Plotfig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 6), dpi=80)plot_acf(df.traffic.tolist(), ax=ax1, lags=50)plot_pacf(df.traffic.tolist(), ax=ax2, lags=20)# Decorate# lighten the bordersax1.spines[\"top\"].set_alpha(.3); ax2.spines[\"top\"].set_alpha(.3)ax1.spines[\"bottom\"].set_alpha(.3); ax2.spines[\"bottom\"].set_alpha(.3)ax1.spines[\"right\"].set_alpha(.3); ax2.spines[\"right\"].set_alpha(.3)ax1.spines[\"left\"].set_alpha(.3); ax2.spines[\"left\"].set_alpha(.3)# font size of tick labelsax1.tick_params(axis='both', labelsize=12)ax2.tick_params(axis='both', labelsize=12)plt.show() 【38】交叉相关图(Cross Correlation plot)交叉相关图显示了两个时间序列相互之间的滞后。 12345678910111213141516171819202122232425262728import statsmodels.tsa.stattools as stattools# Import Datadf = pd.read_csv('https://github.com/selva86/datasets/raw/master/mortality.csv')x = df['mdeaths']y = df['fdeaths']# Compute Cross Correlationsccs = stattools.ccf(x, y)[:100]nlags = len(ccs)# Compute the Significance level# ref: https://stats.stackexchange.com/questions/3115/cross-correlation-significance-in-r/3128#3128conf_level = 2 / np.sqrt(nlags)# Draw Plotplt.figure(figsize=(12, 7), dpi=80)plt.hlines(0, xmin=0, xmax=100, color='gray') # 0 axisplt.hlines(conf_level, xmin=0, xmax=100, color='gray')plt.hlines(-conf_level, xmin=0, xmax=100, color='gray')plt.bar(x=np.arange(len(ccs)), height=ccs, width=.3)# Decorationplt.title('$Cross\\; Correlation\\; Plot:\\; mdeaths\\; vs\\; fdeaths$', fontsize=22)plt.xlim(0, len(ccs))plt.show() 【39】时间序列分解图(Time Series Decomposition Plot)时间序列分解图将时间序列分解为趋势、季节和残差分量。 123456789101112131415from statsmodels.tsa.seasonal import seasonal_decomposefrom dateutil.parser import parse# Import Datadf = pd.read_csv('https://github.com/selva86/datasets/raw/master/AirPassengers.csv')dates = pd.DatetimeIndex([parse(d).strftime('%Y-%m-01') for d in df['date']])df.set_index(dates, inplace=True)# Decomposeresult = seasonal_decompose(df['traffic'], model='multiplicative')# Plotplt.rcParams.update({'figure.figsize': (10, 10)})result.plot().suptitle('Time Series Decomposition of Air Passengers')plt.show() 【40】多重时间序列(Multiple Time Series)您可以在同一图表上绘制多个测量相同值的时间序列,如下所示。 12345678910111213141516171819202122232425262728293031323334353637# Import Datadf = pd.read_csv('https://github.com/selva86/datasets/raw/master/mortality.csv')# Define the upper limit, lower limit, interval of Y axis and colorsy_LL = 100y_UL = int(df.iloc[:, 1:].max().max() * 1.1)y_interval = 400mycolors = ['tab:red', 'tab:blue', 'tab:green', 'tab:orange']# Draw Plot and Annotatefig, ax = plt.subplots(1, 1, figsize=(16, 9), dpi=80)columns = df.columns[1:]for i, column in enumerate(columns): plt.plot(df.date.values, df[column].values, lw=1.5, color=mycolors[i]) plt.text(df.shape[0] + 1, df[column].values[-1], column, fontsize=14, color=mycolors[i])# Draw Tick linesfor y in range(y_LL, y_UL, y_interval): plt.hlines(y, xmin=0, xmax=71, colors='black', alpha=0.3, linestyles=\"--\", lw=0.5)# Decorationsplt.tick_params(axis=\"both\", which=\"both\", bottom=False, top=False, labelbottom=True, left=False, right=False, labelleft=True)# Lighten bordersplt.gca().spines[\"top\"].set_alpha(.3)plt.gca().spines[\"bottom\"].set_alpha(.3)plt.gca().spines[\"right\"].set_alpha(.3)plt.gca().spines[\"left\"].set_alpha(.3)plt.title('Number of Deaths from Lung Diseases in the UK (1974-1979)', fontsize=22)plt.yticks(range(y_LL, y_UL, y_interval), [str(y) for y in range(y_LL, y_UL, y_interval)], fontsize=12)plt.xticks(range(0, df.shape[0], 12), df.date.values[::12], horizontalalignment='left', fontsize=12)plt.ylim(y_LL, y_UL)plt.xlim(-2, 80)plt.show() 【41】使用次要的 Y 轴来绘制不同范围的图形(Plotting with different scales using secondary Y axis)如果要显示在同一时间点测量两个不同数量的两个时间序列,则可以在右侧的次要 Y 轴上再绘制第二个系列。 12345678910111213141516171819202122232425262728293031# Import Datadf = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/economics.csv\")x = df['date']y1 = df['psavert']y2 = df['unemploy']# Plot Line1 (Left Y Axis)fig, ax1 = plt.subplots(1, 1, figsize=(16, 9), dpi=80)ax1.plot(x, y1, color='tab:red')# Plot Line2 (Right Y Axis)ax2 = ax1.twinx() # instantiate a second axes that shares the same x-axisax2.plot(x, y2, color='tab:blue')# Decorations# ax1 (left Y axis)ax1.set_xlabel('Year', fontsize=20)ax1.tick_params(axis='x', rotation=0, labelsize=12)ax1.set_ylabel('Personal Savings Rate', color='tab:red', fontsize=20)ax1.tick_params(axis='y', rotation=0, labelcolor='tab:red')ax1.grid(alpha=.4)# ax2 (right Y axis)ax2.set_ylabel(\"# Unemployed (1000's)\", color='tab:blue', fontsize=20)ax2.tick_params(axis='y', labelcolor='tab:blue')ax2.set_xticks(np.arange(0, len(x), 60))ax2.set_xticklabels(x[::60], rotation=90, fontdict={'fontsize': 10})ax2.set_title(\"Personal Savings Rate vs Unemployed: Plotting in Secondary Y Axis\", fontsize=22)fig.tight_layout()plt.show() 【42】带误差带的时间序列(Time Series with Error Bands)如果您有一个时间序列数据集,其中每个时间点(日期/时间戳)有多个观测值,则可以构造具有误差带的时间序列。下面您可以看到一些基于一天中不同时间的订单的示例。还有一个关于45天内到达的订单数量的例子。 在这种方法中,订单数量的平均值用白线表示。并计算95%的置信区间,并围绕平均值绘制。 1234567891011121314151617181920212223242526272829303132from scipy.stats import sem# Import Datadf = pd.read_csv(\"https://raw.githubusercontent.com/selva86/datasets/master/user_orders_hourofday.csv\")df_mean = df.groupby('order_hour_of_day').quantity.mean()df_se = df.groupby('order_hour_of_day').quantity.apply(sem).mul(1.96)# Plotplt.figure(figsize=(16, 10), dpi=80)plt.ylabel(\"# Orders\", fontsize=16)x = df_mean.indexplt.plot(x, df_mean, color=\"white\", lw=2)plt.fill_between(x, df_mean - df_se, df_mean + df_se, color=\"#3F5D7D\")# Decorations# Lighten bordersplt.gca().spines[\"top\"].set_alpha(0)plt.gca().spines[\"bottom\"].set_alpha(1)plt.gca().spines[\"right\"].set_alpha(0)plt.gca().spines[\"left\"].set_alpha(1)plt.xticks(x[::2], [str(d) for d in x[::2]], fontsize=12)plt.title(\"User Orders by Hour of Day (95% confidence)\", fontsize=22)plt.xlabel(\"Hour of Day\")s, e = plt.gca().get_xlim()plt.xlim(s, e)# Draw Horizontal Tick linesfor y in range(8, 20, 2): plt.hlines(y, xmin=s, xmax=e, colors='black', alpha=0.5, linestyles=\"--\", lw=0.5)plt.show() 1234567891011121314151617181920212223242526272829303132333435363738\"Data Source: https://www.kaggle.com/olistbr/brazilian-ecommerce#olist_orders_dataset.csv\"from dateutil.parser import parsefrom scipy.stats import sem# Import Datadf_raw = pd.read_csv('https://raw.githubusercontent.com/selva86/datasets/master/orders_45d.csv', parse_dates=['purchase_time', 'purchase_date'])# Prepare Data: Daily Mean and SE Bandsdf_mean = df_raw.groupby('purchase_date').quantity.mean()df_se = df_raw.groupby('purchase_date').quantity.apply(sem).mul(1.96)# Plotplt.figure(figsize=(16, 10), dpi=80)plt.ylabel(\"# Daily Orders\", fontsize=16)x = [d.date().strftime('%Y-%m-%d') for d in df_mean.index]plt.plot(x, df_mean, color=\"white\", lw=2)plt.fill_between(x, df_mean - df_se, df_mean + df_se, color=\"#3F5D7D\")# Decorations# Lighten bordersplt.gca().spines[\"top\"].set_alpha(0)plt.gca().spines[\"bottom\"].set_alpha(1)plt.gca().spines[\"right\"].set_alpha(0)plt.gca().spines[\"left\"].set_alpha(1)plt.xticks(x[::6], [str(d) for d in x[::6]], fontsize=12)plt.title(\"Daily Order Quantity of Brazilian Retail with Error Bands (95% confidence)\", fontsize=20)# Axis limitss, e = plt.gca().get_xlim()plt.xlim(s, e - 2)plt.ylim(4, 10)# Draw Horizontal Tick linesfor y in range(5, 10, 1): plt.hlines(y, xmin=s, xmax=e, colors='black', alpha=0.5, linestyles=\"--\", lw=0.5)plt.show() 【43】堆积面积图(Stacked Area Chart)堆积面积图提供了多个时间序列的贡献程度的可视化表示,以便相互比较。 12345678910111213141516171819202122232425262728293031323334353637383940414243# Import Datadf = pd.read_csv('https://raw.githubusercontent.com/selva86/datasets/master/nightvisitors.csv')# Decide Colorsmycolors = ['tab:red', 'tab:blue', 'tab:green', 'tab:orange', 'tab:brown', 'tab:grey', 'tab:pink', 'tab:olive']# Draw Plot and Annotatefig, ax = plt.subplots(1, 1, figsize=(16, 9), dpi=80)columns = df.columns[1:]labs = columns.values.tolist()# Prepare datax = df['yearmon'].values.tolist()y0 = df[columns[0]].values.tolist()y1 = df[columns[1]].values.tolist()y2 = df[columns[2]].values.tolist()y3 = df[columns[3]].values.tolist()y4 = df[columns[4]].values.tolist()y5 = df[columns[5]].values.tolist()y6 = df[columns[6]].values.tolist()y7 = df[columns[7]].values.tolist()y = np.vstack([y0, y2, y4, y6, y7, y5, y1, y3])# Plot for each columnlabs = columns.values.tolist()ax = plt.gca()ax.stackplot(x, y, labels=labs, colors=mycolors, alpha=0.8)# Decorationsax.set_title('Night Visitors in Australian Regions', fontsize=18)ax.set(ylim=[0, 100000])ax.legend(fontsize=10, ncol=4)plt.xticks(x[::5], fontsize=10, horizontalalignment='center')plt.yticks(np.arange(10000, 100000, 20000), fontsize=10)plt.xlim(x[0], x[-1])# Lighten bordersplt.gca().spines[\"top\"].set_alpha(0)plt.gca().spines[\"bottom\"].set_alpha(.3)plt.gca().spines[\"right\"].set_alpha(0)plt.gca().spines[\"left\"].set_alpha(.3)plt.show() 【44】未堆积面积图(Area Chart UnStacked)未堆积的面积图用于可视化两个或多个序列彼此之间的进度(起伏)。在下面的图表中,你可以清楚地看到,随着失业持续时间的中位数增加,个人储蓄率是如何下降的。未堆积面积图很好地展示了这一现象。 123456789101112131415161718192021222324252627282930313233# Import Datadf = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/economics.csv\")# Prepare Datax = df['date'].values.tolist()y1 = df['psavert'].values.tolist()y2 = df['uempmed'].values.tolist()mycolors = ['tab:red', 'tab:blue', 'tab:green', 'tab:orange', 'tab:brown', 'tab:grey', 'tab:pink', 'tab:olive']columns = ['psavert', 'uempmed']# Draw Plotfig, ax = plt.subplots(1, 1, figsize=(16, 9), dpi=80)ax.fill_between(x, y1=y1, y2=0, label=columns[1], alpha=0.5, color=mycolors[1], linewidth=2)ax.fill_between(x, y1=y2, y2=0, label=columns[0], alpha=0.5, color=mycolors[0], linewidth=2)# Decorationsax.set_title('Personal Savings Rate vs Median Duration of Unemployment', fontsize=18)ax.set(ylim=[0, 30])ax.legend(loc='best', fontsize=12)plt.xticks(x[::50], fontsize=10, horizontalalignment='center')plt.yticks(np.arange(2.5, 30.0, 2.5), fontsize=10)plt.xlim(-10, x[-1])# Draw Tick linesfor y in np.arange(2.5, 30.0, 2.5): plt.hlines(y, xmin=0, xmax=len(x), colors='black', alpha=0.3, linestyles=\"--\", lw=0.5)# Lighten bordersplt.gca().spines[\"top\"].set_alpha(0)plt.gca().spines[\"bottom\"].set_alpha(.3)plt.gca().spines[\"right\"].set_alpha(0)plt.gca().spines[\"left\"].set_alpha(.3)plt.show() 【45】日历热力图(Calendar Heat Map)与时间序列相比,日历地图是另一种基于时间的数据可视化的不太受欢迎的方法。虽然在视觉上很吸引人,但数值并不十分明显。然而,它能很好地描绘极端值和假日效果。 【译者 TRHX 注:在使用该方法时要先安装 calmap 库】 123456789101112import matplotlib as mplimport calmap# Import Datadf = pd.read_csv(\"https://raw.githubusercontent.com/selva86/datasets/master/yahoo.csv\", parse_dates=['date'])df.set_index('date', inplace=True)# Plotplt.figure(figsize=(16, 10), dpi=80)calmap.calendarplot(df['2014']['VIX.Close'], fig_kws={'figsize': (16, 10)}, yearlabel_kws={'color': 'black', 'fontsize': 14}, subplot_kws={'title': 'Yahoo Stock Prices'})plt.show() 【46】季节图(Seasonal Plot)季节图可用于比较上一季度同一天(年/月/周等)时间序列的表现。 1234567891011121314151617181920212223242526272829303132333435363738from dateutil.parser import parse# Import Datadf = pd.read_csv('https://github.com/selva86/datasets/raw/master/AirPassengers.csv')# Prepare datadf['year'] = [parse(d).year for d in df.date]df['month'] = [parse(d).strftime('%b') for d in df.date]years = df['year'].unique()# 译者 TRHX 添加了该行代码df.rename(columns={'value': 'traffic'}, inplace=True)# Draw Plotmycolors = ['tab:red', 'tab:blue', 'tab:green', 'tab:orange', 'tab:brown', 'tab:grey', 'tab:pink', 'tab:olive', 'deeppink', 'steelblue', 'firebrick', 'mediumseagreen']plt.figure(figsize=(16, 10), dpi=80)for i, y in enumerate(years): plt.plot('month', 'traffic', data=df.loc[df.year == y, :], color=mycolors[i], label=y) plt.text(df.loc[df.year == y, :].shape[0] - .9, df.loc[df.year == y, 'traffic'][-1:].values[0], y, fontsize=12, color=mycolors[i])# Decorationplt.ylim(50, 750)plt.xlim(-0.3, 11)plt.ylabel('$Air Traffic$')plt.yticks(fontsize=12, alpha=.7)plt.title(\"Monthly Seasonal Plot: Air Passengers Traffic (1949 - 1969)\", fontsize=22)plt.grid(axis='y', alpha=.3)# Remove bordersplt.gca().spines[\"top\"].set_alpha(0.0)plt.gca().spines[\"bottom\"].set_alpha(0.5)plt.gca().spines[\"right\"].set_alpha(0.0)plt.gca().spines[\"left\"].set_alpha(0.5)# plt.legend(loc='upper right', ncol=2, fontsize=12)plt.show() 【9x00】分组( Groups)【47】树状图(Dendrogram)树状图根据给定的距离度量将相似的点组合在一起,并根据点的相似性将它们组织成树状链接。 123456789101112import scipy.cluster.hierarchy as shc# Import Datadf = pd.read_csv('https://raw.githubusercontent.com/selva86/datasets/master/USArrests.csv')# Plotplt.figure(figsize=(16, 10), dpi=80)plt.title(\"USArrests Dendograms\", fontsize=22)dend = shc.dendrogram(shc.linkage(df[['Murder', 'Assault', 'UrbanPop', 'Rape']], method='ward'), labels=df.State.values, color_threshold=100)plt.xticks(fontsize=12)plt.show() 【48】聚类图(Cluster Plot)聚类图可以用来划分属于同一个聚类的点。下面是一个基于 USArrests 数据集将美国各州分成 5 组的代表性示例。这个聚类图使用 ‘murder’ 和 ‘assault’ 作为 X 轴和 Y 轴。或者,您可以将第一个主元件用作 X 轴和 Y 轴。 【译者 TRHX 注:在使用该方法时要先安装 sklearn 库】 1234567891011121314151617181920212223242526272829303132333435from sklearn.cluster import AgglomerativeClusteringfrom scipy.spatial import ConvexHull# Import Datadf = pd.read_csv('https://raw.githubusercontent.com/selva86/datasets/master/USArrests.csv')# Agglomerative Clusteringcluster = AgglomerativeClustering(n_clusters=5, affinity='euclidean', linkage='ward')cluster.fit_predict(df[['Murder', 'Assault', 'UrbanPop', 'Rape']])# Plotplt.figure(figsize=(14, 10), dpi=80)plt.scatter(df.iloc[:, 0], df.iloc[:, 1], c=cluster.labels_, cmap='tab10')# Encircledef encircle(x, y, ax=None, **kw): if not ax: ax = plt.gca() p = np.c_[x, y] hull = ConvexHull(p) poly = plt.Polygon(p[hull.vertices,:], **kw) ax.add_patch(poly)# Draw polygon surrounding verticesencircle(df.loc[cluster.labels_ == 0, 'Murder'], df.loc[cluster.labels_ == 0, 'Assault'], ec=\"k\", fc=\"gold\", alpha=0.2, linewidth=0)encircle(df.loc[cluster.labels_ == 1, 'Murder'], df.loc[cluster.labels_ == 1, 'Assault'], ec=\"k\", fc=\"tab:blue\", alpha=0.2, linewidth=0)encircle(df.loc[cluster.labels_ == 2, 'Murder'], df.loc[cluster.labels_ == 2, 'Assault'], ec=\"k\", fc=\"tab:red\", alpha=0.2, linewidth=0)encircle(df.loc[cluster.labels_ == 3, 'Murder'], df.loc[cluster.labels_ == 3, 'Assault'], ec=\"k\", fc=\"tab:green\", alpha=0.2, linewidth=0)encircle(df.loc[cluster.labels_ == 4, 'Murder'], df.loc[cluster.labels_ == 4, 'Assault'], ec=\"k\", fc=\"tab:orange\", alpha=0.2, linewidth=0)# Decorationsplt.xlabel('Murder'); plt.xticks(fontsize=12)plt.ylabel('Assault'); plt.yticks(fontsize=12)plt.title('Agglomerative Clustering of USArrests (5 Groups)', fontsize=22)plt.show() 【49】安德鲁斯曲线(Andrews Curve)安德鲁斯曲线有助于可视化是否存在基于给定分组的数值特征的固有分组。如果特征(数据集中的列)不能帮助区分组(cyl),则行将不会像下图所示被很好地分隔开。 12345678910111213141516171819202122from pandas.plotting import andrews_curves# Importdf = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mtcars.csv\")df.drop(['cars', 'carname'], axis=1, inplace=True)# Plotplt.figure(figsize=(12, 9), dpi=80)andrews_curves(df, 'cyl', colormap='Set1')# Lighten bordersplt.gca().spines[\"top\"].set_alpha(0)plt.gca().spines[\"bottom\"].set_alpha(.3)plt.gca().spines[\"right\"].set_alpha(0)plt.gca().spines[\"left\"].set_alpha(.3)plt.title('Andrews Curves of mtcars', fontsize=22)plt.xlim(-3, 3)plt.grid(alpha=0.3)plt.xticks(fontsize=12)plt.yticks(fontsize=12)plt.show() 【50】平行坐标图(Parallel Coordinates)平行坐标有助于可视化功能是否有助于有效地隔离组。如果一个分离受到影响,则该特征可能在预测该组时非常有用。 1234567891011121314151617181920from pandas.plotting import parallel_coordinates# Import Datadf_final = pd.read_csv(\"https://raw.githubusercontent.com/selva86/datasets/master/diamonds_filter.csv\")# Plotplt.figure(figsize=(12, 9), dpi=80)parallel_coordinates(df_final, 'cut', colormap='Dark2')# Lighten bordersplt.gca().spines[\"top\"].set_alpha(0)plt.gca().spines[\"bottom\"].set_alpha(.3)plt.gca().spines[\"right\"].set_alpha(0)plt.gca().spines[\"left\"].set_alpha(.3)plt.title('Parallel Coordinated of Diamonds', fontsize=22)plt.grid(alpha=0.3)plt.xticks(fontsize=12)plt.yticks(fontsize=12)plt.show() 1234这里是一段防爬虫文本,请读者忽略。本译文首发于 CSDN,作者 Selva Prabhakaran,译者 TRHX。本文链接:https://itrhx.blog.csdn.net/article/details/106558131原文链接:https://www.machinelearningplus.com/plots/top-50-matplotlib-visualizations-the-master-plots-python/","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Matplotlib","slug":"Python-数据分析/Matplotlib","permalink":"https://www.itrhx.com/categories/Python-数据分析/Matplotlib/"}],"tags":[{"name":"Matplotlib","slug":"Matplotlib","permalink":"https://www.itrhx.com/tags/Matplotlib/"}]},{"title":"Python 数据分析三剑客之 Matplotlib(十):3D 图的绘制","slug":"A77-Matplotlib-10","date":"2020-06-07T16:01:53.824Z","updated":"2020-08-06T03:23:28.610Z","comments":true,"path":"2020/06/08/A77-Matplotlib-10/","link":"","permalink":"https://www.itrhx.com/2020/06/08/A77-Matplotlib-10/","excerpt":"","text":"Matplotlib 系列文章: Python 数据分析三剑客之 Matplotlib(一):初识 Matplotlib 与其 matplotibrc 配置文件 Python 数据分析三剑客之 Matplotlib(二):文本描述 / 中文支持 / 画布 / 网格等基本图像属性 Python 数据分析三剑客之 Matplotlib(三):图例 / LaTeX / 刻度 / 子图 / 补丁等基本图像属性 Python 数据分析三剑客之 Matplotlib(四):线性图的绘制 Python 数据分析三剑客之 Matplotlib(五):散点图的绘制 Python 数据分析三剑客之 Matplotlib(六):直方图 / 柱状图 / 条形图的绘制 Python 数据分析三剑客之 Matplotlib(七):饼状图的绘制 Python 数据分析三剑客之 Matplotlib(八):等高线 / 等值线图的绘制 Python 数据分析三剑客之 Matplotlib(九):极区图 / 极坐标图 / 雷达图的绘制 Python 数据分析三剑客之 Matplotlib(十):3D 图的绘制 Python 数据分析三剑客之 Matplotlib(十一):最热门最常用的 50 个图表【译文】 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106558131未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【01x00】了解 mplot3d Toolkitmplot3d Toolkit 即 mplot3d 工具包,在 matplotlib 中使用 mplot3d 工具包可以绘制 3D 图。 mplot3d 官方文档:https://matplotlib.org/tutorials/toolkits/mplot3d.html 在 matplotlib 中,figure 为画布,axes 为绘图区,fig.add_subplot()、plt.subplot() 方法均可以创建子图,在绘制 3D 图时,某些 2D 图的参数也适用于 3D 图,在本文的示例中,可能会用到的一些没有具体解释的函数或者参数,其用法均可在前面的系列文章中找到: 《Python 数据分析三剑客之 Matplotlib(一):初识 Matplotlib 与其 matplotibrc 配置文件》 《Python 数据分析三剑客之 Matplotlib(二):文本描述 / 中文支持 / 画布 / 网格等基本图像属性》 《Python 数据分析三剑客之 Matplotlib(三):图例 / LaTeX / 刻度 / 子图 / 补丁等基本图像属性》 绘制 3D 图的步骤:创建 Axes3D 对象,然后调用 Axes3D 的不同方法来绘制不同类型的 3D 图。以下介绍三种 Axes3D 对象创建的方法。 【01x01】Axes3D 对象创建方法一:Axes3D(fig)在 Matplotlib 1.0.0 版本中,绘制 3D 图需要先导入 Axes3D 包,获取 figure 画布对象 fig 后,通过 Axes3D(fig) 方法来创建 Axes3D 对象,具体方法如下: 12345678910111213141516import numpy as npimport matplotlib.pyplot as pltfrom mpl_toolkits.mplot3d import Axes3D# 获取 figure 画布并创建 Axes3D 对象fig = plt.figure()ax = Axes3D(fig)# 数据坐标z = np.linspace(0, 15, 1000)x = np.sin(z)y = np.cos(z)# 绘制线性图ax.plot(x, y, z)plt.show() 【01x02】Axes3D 对象创建方法二:add_subplot在 Matplotlib 3.2.0 版本中,绘制 3D 图可以通过创建子图,然后指定 projection 参数 为 3d 即可,返回的 ax 为 Axes3D 对象,以下两种方法均可: 123456789101112131415import numpy as npimport matplotlib.pyplot as plt# 获取 figure 画布并通过子图创建 Axes3D 对象fig = plt.figure()ax = fig.add_subplot(111, projection='3d')# 数据坐标z = np.linspace(0, 15, 1000)x = np.sin(z)y = np.cos(z)# 绘制线性图ax.plot(x, y, z)plt.show() 1234567891011121314import numpy as npimport matplotlib.pyplot as plt# 通过子图创建 Axes3D 对象ax = plt.subplot(111, projection='3d')# 数据坐标z = np.linspace(0, 15, 1000)x = np.sin(z)y = np.cos(z)# 绘制线性图ax.plot(x, y, z)plt.show() 【01x03】Axes3D 对象创建方法三:gca除了以上两种方法以外,还可以先获取画布对象 fig,再通过 fig.gca() 方法获取当前绘图区(gca = Get Current Axes),然后指定 projection 参数 为 3d 即可,返回的 ax 为 Axes3D 对象。 123456789101112131415import numpy as npimport matplotlib.pyplot as plt# 依次获取画布和绘图区并创建 Axes3D 对象fig = plt.figure()ax = fig.gca(projection='3d')# 数据坐标z = np.linspace(0, 15, 1000)x = np.sin(z)y = np.cos(z)# 绘制线性图ax.plot(x, y, z)plt.show() 以上三种方法运行结果均为下图: 【02x00】cmap 与 colorbar默认情况下,散点图、线性图、曲面图等将以纯色着色,但可以通过提供 cmap 参数支持颜色映射。cmap 参数用于设置一些特殊的颜色组合,如渐变色等,参数取值通常为 Colormap 中的值,具体取值可参见下图: 官方文档:https://matplotlib.org/tutorials/colors/colormaps.html 如果使用了 cmap 参数,则可以使用 pyplot.colorbar() 函数来绘制一个色条,即颜色对照条。 基本语法:matplotlib.pyplot.colorbar([mappable=None, cax=None, ax=None, **kw]) 部分参数解释如下表,其他参数,如长度,宽度等请参考官方文档。 参数 描述 mappable 要设置色条的图像对象,该参数对于 Figure.colorbar 方法是必需的,但对于 pyplot.colorbar 函数是可选的 cax 可选项,要绘制色条的轴 ax 可选项,设置色条的显示位置,通常在一个画布上有多个子图时使用 **kw 可选项,其他关键字参数,参考官方文档 【03x00】3D 线性图:Axes3D.plot基本方法:Axes3D.plot(xs, ys[, zs, zdir='z', *args, **kwargs]) 参数 描述 xs 一维数组,点的 x 轴坐标 ys 一维数组,点的 y 轴坐标 zs 一维数组,可选项,点的 z 轴坐标 zdir 可选项,在 3D 轴上绘制 2D 数据时,数据必须以 xs,ys 的形式传递,若此时将 zdir 设置为 ‘y’,数据将会被绘制到 x-z 轴平面上,默认为 ‘z’ **kwargs 其他关键字参数,可选项,可参见 matplotlib.axes.Axes.plot 12345678910111213141516171819202122232425262728293031323334import numpy as npimport matplotlib.pyplot as plt# 设置中文显示plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']# 依次获取画布和绘图区并创建 Axes3D 对象fig = plt.figure()ax = fig.gca(projection='3d')# 第一条3D线性图数据theta = np.linspace(-4 * np.pi, 4 * np.pi, 100)z1 = np.linspace(-2, 2, 100)r = z1**2 + 1x1 = r * np.sin(theta)y1 = r * np.cos(theta)# 第二条3D线性图数据z2 = np.linspace(-3, 3, 100)x2 = np.sin(z2)y2 = np.cos(z2)# 绘制3D线性图ax.plot(x1, y1, z1, color='b', label='3D 线性图一')ax.plot(x2, y2, z2, color='r', label='3D 线性图二')# 设置标题、轴标签、图例,也可以直接使用 plt.title、plt.xlabel、plt.legend...ax.set_title('绘制 3D 线性图示例', pad=15, fontsize='12')ax.set_xlabel('x 轴', color='r', fontsize='12')ax.set_ylabel('y 轴', color='g', fontsize='12')ax.set_zlabel('z 轴', color='b', fontsize='12')ax.legend()plt.show() 【04x00】3D 散点图:Axes3D.scatter基本方法:Axes3D.scatter(xs, ys[, zs=0, zdir='z', s=20, c=None, depthshade=True, *args, **kwargs]) 参数 描述 xs 一维数组,点的 x 轴坐标 ys 一维数组,点的 y 轴坐标 zs 一维数组,可选项,点的 z 轴坐标 zdir 可选项,在 3D 轴上绘制 2D 数据时,数据必须以 xs,ys 的形式传递,若此时将 zdir 设置为 ‘y’,数据将会被绘制到 x-z 轴平面上,默认为 ‘z’ s 标量或数组类型,可选项,标记的大小,默认 20 c 标记的颜色,可选项,可以是单个颜色或者一个颜色列表支持英文颜色名称及其简写、十六进制颜色码等,更多颜色示例参见官网 Color Demo depthshade bool 值,可选项,默认 True,是否为散点标记着色以提供深度外观 **kwargs 其他关键字参数,可选项,可参见 scatter 1234567891011121314151617181920212223242526272829303132import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']# 依次获取画布和绘图区并创建 Axes3D 对象fig = plt.figure()ax = fig.gca(projection='3d')n = 100def randrange(n, vmin, vmax): return (vmax - vmin)*np.random.rand(n) + vmin'''定义绘制 n 个随机点,设置每一组数据点的样式和范围x轴数据位于[23,32]区间,y轴数据位于[0,100]区间,z轴数据位于[zlow,zhigh]区间'''for m, zlow, zhigh in [('o', -50, -25), ('^', -30, -5)]: xs = randrange(n, 23, 32) ys = randrange(n, 0, 100) zs = randrange(n, zlow, zhigh) ax.scatter(xs, ys, zs, marker=m)# 设置标题、轴标签、图例,也可以直接使用 plt.title、plt.xlabel...ax.set_title('绘制 3D 散点图示例', pad=15, fontsize='12')ax.set_xlabel('x 轴', color='b')ax.set_ylabel('y 轴', color='b')ax.set_zlabel('z 轴', color='b')plt.show() 【05x00】3D 线框图:Axes3D.plot_wireframe基本方法:Axes3D.plot_wireframe(X, Y, Z[, *args, **kwargs]) 参数 描述 X 二维数组,x 轴数据 Y 二维数组,y 轴数据 Z 二维数组,z 轴数据 **kwargs 其他关键字参数,可选项,如线条样式颜色等,可参见 Line3DCollection 1234567891011121314151617181920212223242526272829303132import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']# 获取 figure 画布并通过子图创建 Axes3D 对象fig = plt.figure()ax = fig.add_subplot(111, projection='3d')# 定义Z轴坐标的生成方法def f(m, n): return np.sin(np.sqrt(m ** 2 + n ** 2))# 设置3D线框图数据x = np.linspace(-6, 6, 30)y = np.linspace(-6, 6, 30)# 生成网格点坐标矩阵,该方法在系列文章八中有具体介绍X, Y = np.meshgrid(x, y)Z = f(X, Y)# 绘制3D线框图ax.plot_wireframe(X, Y, Z, color='c')# 设置标题、轴标签、图例,也可以直接使用 plt.title、plt.xlabel...ax.set_title('绘制 3D 线框图示例', pad=15, fontsize='12')ax.set_xlabel('x 轴')ax.set_ylabel('y 轴')ax.set_zlabel('z 轴')plt.show() 【06x00】3D 曲面图:Axes3D.plot_surface基本方法:Axes3D.plot_surface(X, Y, Z[, *args, vmin=None, vmax=None, **kwargs]) 参数 描述 X 二维数组,x 轴数据 Y 二维数组,y 轴数据 Z 二维数组,z 轴数据 vmin / vmax 规定数据界限 **kwargs 其他关键字参数,可选项,如线条样式颜色等,可参见 Line3DCollection 1234567891011121314151617181920212223242526272829303132import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']# 获取 figure 画布并通过子图创建 Axes3D 对象fig = plt.figure()ax = fig.add_subplot(111, projection='3d')# 设置3D曲面图数据X = np.arange(-5, 5, 0.25)Y = np.arange(-5, 5, 0.25)# 生成网格点坐标矩阵,该方法在系列文章八中有具体介绍X, Y = np.meshgrid(X, Y)R = np.sqrt(X**2 + Y**2)Z = np.sin(R)# 绘制3D曲面图并添加色条(长度0.8)surface = ax.plot_surface(X, Y, Z, cmap='rainbow', antialiased=False)fig.colorbar(surface, shrink=0.8)# 设置标题、轴标签、图例,也可以直接使用 plt.title、plt.xlabel...ax.set_title('绘制 3D 曲面图示例', pad=15, fontsize='12')ax.set_xlabel('x 轴')ax.set_ylabel('y 轴')ax.set_zlabel('z 轴')# 调整观察角度和方位角,俯仰角25度,方位角40度ax.view_init(25, 40)# 设置Z轴刻度界限ax.set_zlim(-2, 2)plt.show() 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106558131未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【07x00】3D 柱状图:Axes3D.bar基本方法:Axes3D.bar(left, height, zs=0, zdir='z', *args, **kwargs) 参数 描述 left 一维数组,柱状图最左侧位置的 x 坐标 height 一维数组,柱状图的高度(y 坐标) zs 第 i 个多边形将出现在平面 y=zs[i] 上 zdir 可选项,在 3D 轴上绘制 2D 数据时,数据必须以 xs,ys 的形式传递,若此时将 zdir 设置为 ‘y’,数据将会被绘制到 x-z 轴平面上,默认为 ‘z’ **kwargs 其他关键字参数,参见 matplotlib.axes.Axes.bar 123456789101112131415161718192021222324252627import matplotlib.pyplot as pltimport numpy as npplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']# 获取 figure 画布并通过子图创建 Axes3D 对象fig = plt.figure()ax = fig.add_subplot(111, projection='3d')colors = ['r', 'g', 'b', 'y']yticks = [3, 2, 1, 0]# 设置3D柱状图数据并绘制图像for c, k in zip(colors, yticks): xs = np.arange(20) ys = np.random.rand(20) cs = [c] * len(xs) ax.bar(xs, ys, zs=k, zdir='y', color=cs, alpha=0.8)# 设置图像标题、坐标标签以及范围ax.set_title('绘制 3D 柱状图示例', pad=15, fontsize='12')ax.set_xlabel('X 轴')ax.set_ylabel('Y 轴')ax.set_zlabel('Z 轴')ax.set_yticks(yticks)plt.show() 【08x00】3D 箭头图:Axes3D.quiver基本方法:Axes3D.quiver(X, Y, Z, U, V, W, length=1, arrow_length_ratio=0.3, pivot='tail', normalize=False, **kwargs) 参数 描述 X, Y, Z 数组形式,箭头位置的 x、y 和 z 轴坐标(默认为箭头尾部) U, V, W 数组形式,箭头向量的 x、y 和 z 轴分量 length float 类型,每个箭筒的长度,默认为 1.0 arrow_length_ratio float 类型,箭头相对于箭身的比率,默认为 0.3 pivot 箭头在网格点上的位置;箭头围绕该点旋转,因此命名为 pivot,默认为 ‘tail’可选项:'tail':尾部;'middle':中间;'tip':尖端 normalize bool 类型,如果为 True,则所有箭头的长度都将相同默认为 False,即箭头的长度取决于 U、V、W 的值 **kwargs 其他关键字参数,参见 LineCollection 1234567891011121314151617181920212223242526272829303132import matplotlib.pyplot as pltimport numpy as npplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']# 依次获取画布和绘图区并创建 Axes3D 对象fig = plt.figure()ax = fig.gca(projection='3d')# 设置箭头位置x, y, z = np.meshgrid(np.arange(-0.8, 1, 0.2), np.arange(-0.8, 1, 0.2), np.arange(-0.8, 1, 0.8))# 设置箭头数据u = np.sin(np.pi * x) * np.cos(np.pi * y) * np.cos(np.pi * z)v = -np.cos(np.pi * x) * np.sin(np.pi * y) * np.cos(np.pi * z)w = (np.sqrt(2.0 / 3.0) * np.cos(np.pi * x) * np.cos(np.pi * y) * np.sin(np.pi * z))# 绘制 3D 箭头图ax.quiver(x, y, z, u, v, w, length=0.1, normalize=True)# 设置图像标题、坐标标签ax.set_title('绘制 3D 箭头图示例', pad=15, fontsize='12')ax.set_xlabel('X 轴')ax.set_ylabel('Y 轴')ax.set_zlabel('Z 轴')# 调整观察角度,俯仰角20度ax.view_init(20)plt.show() 【09x00】3D 等高线图:Axes3D.contour基本方法:Axes3D.contour(X, Y, Z[, *args, extend3d=False, stride=5, zdir='z', offset=None, **kwargs]) 参数 描述 X 一维数组,x 轴数据 Y 一维数组,y 轴数据 Z 一维数组,z 轴数据 extend3d bool 值,可选项,是否以 3D 延伸轮廓,默认 False stride int 类型,可选项,用于延伸轮廓的步长 zdir 可选项,在 3D 轴上绘制 2D 数据时,数据必须以 xs,ys 的形式传递,若此时将 zdir 设置为 ‘y’,数据将会被绘制到 x-z 轴平面上,默认为 ‘z’ offset 标量,可选项,如果指定,则在垂直于 zdir 的平面上的位置绘制轮廓线的投影 **kwargs 其他关键字参数,可选项,可参见 matplotlib.axes.Axes.contour 1234567891011121314151617181920212223242526272829303132import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']# 获取 figure 画布并通过子图创建 Axes3D 对象fig = plt.figure(figsize=(8, 4.8))ax = fig.add_subplot(111, projection='3d')# 设置等高线数据X = np.arange(-2.0, 2.0, 0.01)Y = np.arange(-2.0, 2.0, 0.01)# 生成网格点坐标矩阵m, n = np.meshgrid(X, Y)# 指定一个函数用于计算每个点的高度,也可以直接使用二维数组储存每个点的高度def f(a, b): return (1 - b ** 5 + a ** 5) * np.exp(-a ** 2 - b ** 2)# 绘制3D等高线图并添加色条图(长度0.8)contour = ax.contour(X, Y, f(m, n), cmap='rainbow')fig.colorbar(contour, shrink=0.8)# 设置标题、轴标签、图例,也可以直接使用 plt.title、plt.xlabel...ax.set_title('绘制 3D 等高线图示例', pad=15, fontsize='12')ax.set_xlabel('x 轴')ax.set_ylabel('y 轴')ax.set_zlabel('z 轴')plt.show() 【10x00】3D 等高线填充图:Axes3D.contourf基本语法:Axes3D.contourf(X, Y, Z[, *args, zdir='z', offset=None, **kwargs]) 参数 描述 X 一维数组,x 轴数据 Y 一维数组,y 轴数据 Z 一维数组,z 轴数据 zdir 可选项,在 3D 轴上绘制 2D 数据时,数据必须以 xs,ys 的形式传递,若此时将 zdir 设置为 ‘y’,数据将会被绘制到 x-z 轴平面上,默认为 ‘z’ offset 标量,可选项,如果指定,则在垂直于 zdir 的平面上的位置绘制轮廓线的投影 **kwargs 其他关键字参数,可选项,可参见 matplotlib.axes.Axes.contourf 1234567891011121314151617181920212223242526272829303132import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']# 获取 figure 画布并通过子图创建 Axes3D 对象fig = plt.figure(figsize=(8, 4.8))ax = fig.add_subplot(111, projection='3d')# 设置等高线数据X = np.arange(-2.0, 2.0, 0.01)Y = np.arange(-2.0, 2.0, 0.01)# 生成网格点坐标矩阵m, n = np.meshgrid(X, Y)# 指定一个函数用于计算每个点的高度,也可以直接使用二维数组储存每个点的高度def f(a, b): return (1 - b ** 5 + a ** 5) * np.exp(-a ** 2 - b ** 2)# 绘制3D等高线图并添加色条图(长度0.8)contourf = ax.contourf(X, Y, f(m, n), cmap='rainbow')fig.colorbar(contourf, shrink=0.8)# 设置标题、轴标签、图例,也可以直接使用 plt.title、plt.xlabel...ax.set_title('绘制 3D 等高线填充图示例', pad=15, fontsize='12')ax.set_xlabel('x 轴')ax.set_ylabel('y 轴')ax.set_zlabel('z 轴')plt.show() 【11x00】3D 三角曲面图:Axes3D.plot_trisurf基本方法:Axes3D.plot_trisurf(X, Y, Z[, *args, color=None, vmin=None, vmax=None, **kwargs]) 参数 描述 X 一维数组,x 轴数据 Y 一维数组,y 轴数据 Z 一维数组,z 轴数据 color 曲面表面的颜色 vmin / vmax 规定数据界限 **kwargs 可选项,其他关键字参数,可参见 Poly3DCollection 1234567891011121314151617181920212223242526272829import matplotlib.pyplot as pltimport numpy as npplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']# 获取 figure 画布并通过子图创建 Axes3D 对象fig = plt.figure()ax = fig.add_subplot(111, projection='3d')n_radii = 8n_angles = 36radii = np.linspace(0.125, 1.0, n_radii)angles = np.linspace(0, 2*np.pi, n_angles, endpoint=False)[..., np.newaxis]x = np.append(0, (radii*np.cos(angles)).flatten())y = np.append(0, (radii*np.sin(angles)).flatten())z = np.sin(-x*y)# 绘制3D三角曲面图并添加色条(长度0.8)trisurf = ax.plot_trisurf(x, y, z, cmap='rainbow')fig.colorbar(trisurf, shrink=0.8)# 设置标题、轴标签、图例,也可以直接使用 plt.title、plt.xlabel...ax.set_title('绘制 3D 三角曲面图示例', pad=15, fontsize='12')ax.set_xlabel('x 轴')ax.set_ylabel('y 轴')ax.set_zlabel('z 轴')plt.show() 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364import numpy as npimport matplotlib.pyplot as pltimport matplotlib.tri as mtriplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']fig = plt.figure(figsize=(15, 6))# ============ 第一个示例图 ============ #ax = fig.add_subplot(1, 2, 1, projection='3d')u = np.linspace(0, 2.0 * np.pi, endpoint=True, num=50)v = np.linspace(-0.5, 0.5, endpoint=True, num=10)u, v = np.meshgrid(u, v)u, v = u.flatten(), v.flatten()x = (1 + 0.5 * v * np.cos(u / 2.0)) * np.cos(u)y = (1 + 0.5 * v * np.cos(u / 2.0)) * np.sin(u)z = 0.5 * v * np.sin(u / 2.0)tri = mtri.Triangulation(u, v)trisurf_1 = ax.plot_trisurf(x, y, z, triangles=tri.triangles, cmap='cool')fig.colorbar(trisurf_1, shrink=0.8)ax.set_zlim(-1, 1)ax.set_title('绘制 3D 三角曲面图示例一', pad=15, fontsize='12')ax.set_xlabel('x 轴')ax.set_ylabel('y 轴')ax.set_zlabel('z 轴')# ============ 第二个示例图 ============ #ax = fig.add_subplot(1, 2, 2, projection='3d')n_angles = 36n_radii = 8min_radius = 0.25radii = np.linspace(min_radius, 0.95, n_radii)angles = np.linspace(0, 2*np.pi, n_angles, endpoint=False)angles = np.repeat(angles[..., np.newaxis], n_radii, axis=1)angles[:, 1::2] += np.pi/n_anglesx = (radii*np.cos(angles)).flatten()y = (radii*np.sin(angles)).flatten()z = (np.cos(radii)*np.cos(3*angles)).flatten()triang = mtri.Triangulation(x, y)xmid = x[triang.triangles].mean(axis=1)ymid = y[triang.triangles].mean(axis=1)mask = xmid**2 + ymid**2 < min_radius**2triang.set_mask(mask)trisurf_2 = ax.plot_trisurf(triang, z, cmap='hsv')fig.colorbar(trisurf_2, shrink=0.8)ax.set_title('绘制 3D 三角曲面图示例二', pad=15, fontsize='12')ax.set_xlabel('x 轴')ax.set_ylabel('y 轴')ax.set_zlabel('z 轴')plt.show() 【12x00】将 2D 图像聚合到 3D 图像中:Axes3D.add_collection3d基本方法:Axes3D.add_collection3d(col, zs=0, zdir='z') 参数 描述 col PolyCollection / LineCollection / PatchCollection 对象 zs 第 i 个多边形将出现在平面 y=zs[i] 上 zdir 可选项,在 3D 轴上绘制 2D 数据时,数据必须以 xs,ys 的形式传递,若此时将 zdir 设置为 ‘y’,数据将会被绘制到 x-z 轴平面上,默认为 ‘z’ 该函数一般用来向图形中添加 3D 集合对象,以下用一个示例来展示某个地区在不同年份和不同月份的降水量: 12345678910111213141516171819202122232425262728293031import numpy as npimport matplotlib.pyplot as pltfrom matplotlib.collections import PolyCollectionplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']fig = plt.figure()ax = fig.gca(projection='3d')np.random.seed(59)month = np.arange(0, 13)years = [2017, 2018, 2019, 2020]precipitation = []for year in years: value = np.random.rand(len(month)) * 300 value[0], value[-1] = 0, 0 precipitation.append(list(zip(month, value)))poly = PolyCollection(precipitation, facecolors=['r', 'g', 'b', 'y'], alpha=.6)ax.add_collection3d(poly, zs=years, zdir='y')ax.set_title('2D 图像聚合到 3D 图像示例', pad=15, fontsize='12')ax.set_xlabel('月份')ax.set_ylabel('年份')ax.set_zlabel('降水量')ax.set_xlim3d(0, 12)ax.set_ylim3d(2016, 2021)ax.set_zlim3d(0, 300)plt.show() 此外,该方法也常被用于绘制 3D 多边形图,即多边体,示例如下: 1234567891011121314151617181920212223242526272829303132import matplotlib.pyplot as pltfrom mpl_toolkits.mplot3d.art3d import Poly3DCollection, Line3DCollectionplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']fig = plt.figure()ax = fig.gca(projection='3d')# 六面体顶点和面verts = [(0, 0, 0), (0, 1, 0), (1, 1, 0), (1, 0, 0), (0, 0, 1), (0, 1, 1), (1, 1, 1), (1, 0, 1)]faces = [[0, 1, 2, 3], [4, 5, 6, 7], [0, 1, 5, 4], [1, 2, 6, 5], [2, 3, 7, 6], [0, 3, 7, 4]]# 获取每个面的顶点poly3d = [[verts[vert_id] for vert_id in face] for face in faces]# 绘制顶点x, y, z = zip(*verts)ax.scatter(x, y, z)# 绘制多边形面ax.add_collection3d(Poly3DCollection(poly3d, facecolors='w', linewidths=1, alpha=0.5))# 绘制多边形的边ax.add_collection3d(Line3DCollection(poly3d, colors='k', linewidths=0.5, linestyles=':'))# 设置图像标题、坐标标签以及范围ax.set_title('绘制多边体示例', pad=15, fontsize='12')ax.set_xlabel('X 轴')ax.set_ylabel('Y 轴')ax.set_zlabel('Z 轴')ax.set_xlim3d(-0.5, 1.5)ax.set_ylim3d(-0.5, 1.5)ax.set_zlim3d(-0.5, 1.5)plt.show() 【13x00】3D 图添加文本描述:Axes3D.text基本方法:Axes3D.text(x, y, z, s[, zdir=None, **kwargs]) 参数 描述 x, y, z 文本位置的 x、y、z 轴坐标 s 要添加的文本 zdir 可选项,若将 zdir 设置为 ‘y’,文本将会被投影到 x-z 轴平面上,默认为 None **kwargs 其他关键字参数,参见 matplotlib.text 123456789101112131415161718192021222324252627282930313233import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']# 依次获取画布和绘图区并创建 Axes3D 对象fig = plt.figure()ax = fig.gca(projection='3d')# Demo 1: zdir 参数用法zdirs = (None, 'x', 'y', 'z', (1, 1, 0), (1, 1, 1))xs = (1, 4, 4, 9, 4, 1)ys = (2, 5, 8, 10, 1, 2)zs = (10, 3, 8, 9, 1, 8)for zdir, x, y, z in zip(zdirs, xs, ys, zs): label = '(%d, %d, %d), dir=%s' % (x, y, z, zdir) ax.text(x, y, z, label, zdir)# Demo 2:设置颜色ax.text(9, 0, 0, \"red\", color='red')# Demo 3: text2D,位置(0,0)为左下角,(1,1)为右上角。ax.text2D(0.05, 0.95, \"2D Text\", transform=ax.transAxes)# 设置坐标轴界限和标签ax.set_xlim(0, 10)ax.set_ylim(0, 10)ax.set_zlim(0, 10)ax.set_xlabel('X 轴')ax.set_ylabel('Y 轴')ax.set_zlabel('Z 轴')plt.show() 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106558131未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Matplotlib","slug":"Python-数据分析/Matplotlib","permalink":"https://www.itrhx.com/categories/Python-数据分析/Matplotlib/"}],"tags":[{"name":"Matplotlib","slug":"Matplotlib","permalink":"https://www.itrhx.com/tags/Matplotlib/"},{"name":"3D图","slug":"3D图","permalink":"https://www.itrhx.com/tags/3D图/"}]},{"title":"Python 数据分析三剑客之 Matplotlib(九):极区图/极坐标图/雷达图的绘制","slug":"A76-Matplotlib-09","date":"2020-06-03T11:00:14.070Z","updated":"2020-08-06T03:21:28.276Z","comments":true,"path":"2020/06/03/A76-Matplotlib-09/","link":"","permalink":"https://www.itrhx.com/2020/06/03/A76-Matplotlib-09/","excerpt":"","text":"Matplotlib 系列文章: Python 数据分析三剑客之 Matplotlib(一):初识 Matplotlib 与其 matplotibrc 配置文件 Python 数据分析三剑客之 Matplotlib(二):文本描述 / 中文支持 / 画布 / 网格等基本图像属性 Python 数据分析三剑客之 Matplotlib(三):图例 / LaTeX / 刻度 / 子图 / 补丁等基本图像属性 Python 数据分析三剑客之 Matplotlib(四):线性图的绘制 Python 数据分析三剑客之 Matplotlib(五):散点图的绘制 Python 数据分析三剑客之 Matplotlib(六):直方图 / 柱状图 / 条形图的绘制 Python 数据分析三剑客之 Matplotlib(七):饼状图的绘制 Python 数据分析三剑客之 Matplotlib(八):等高线 / 等值线图的绘制 Python 数据分析三剑客之 Matplotlib(九):极区图 / 极坐标图 / 雷达图的绘制 Python 数据分析三剑客之 Matplotlib(十):3D 图的绘制 Python 数据分析三剑客之 Matplotlib(十一):最热门最常用的 50 个图表【译文】 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106162412未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【1x00】了解极坐标参考百度百科:极坐标,属于二维坐标系统,创始人是牛顿,主要应用于数学领域。极坐标是指在平面内取一个定点 O,叫极点,引一条射线 Ox,叫做极轴,再选定一个长度单位和角度的正方向(通常取逆时针方向)。对于平面内任何一点 M,用 ρ 表示线段 OM 的长度(有时也用 r 表示),θ 表示从 Ox 到 OM 的角度,ρ 叫做点 M 的极径,θ 叫做点 M 的极角,有序数对 (ρ,θ) 就叫点 M 的极坐标,这样建立的坐标系叫做极坐标系。通常情况下,M 的极径坐标单位为 1(长度单位),极角坐标单位为 rad(或°)。 【2x00】基本方法 matplotlib.pyplot.polar()matplotlib.pyplot.polar() 方法可用于绘制极坐标图。 基本语法:polar(theta, r, **kwargs) theta:点的角坐标,以弧度单位传入参数; r:点的半径坐标; **kwargs:可选项,其他 Line2D 属性,常用属性见表一。 拓展:数学上通常是用弧度而非角度,弧度单位缩写为 rad,2π rad = 360°,1° ≈ 0.0174533 rad,1 rad ≈ 57.29578°。 角度转换为弧度公式:弧度 = 角度 ÷ 180 × π 弧度转换为角度公式:角度 = 弧度 × 180 ÷ π 表一:Line2D 部分属性,完整属性参见官方文档:https://matplotlib.org/api/_as_gen/matplotlib.lines.Line2D.html 属性 描述 alpha 线条透明度,float 类型,取值范围:[0, 1],默认为 1.0,即不透明 antialiased / aa 是否使用抗锯齿渲染,默认为 True color / c 线条颜色,支持英文颜色名称及其简写、十六进制颜色码等,更多颜色示例参见官网 Color Demo fillstyle 点的填充样式,'full'、'left'、'right'、'bottom'、'top'、'none' label 图例,具体参数参见:《Python 数据分析三剑客之 Matplotlib(三):图例 / LaTeX / 刻度 / 子图 / 补丁等基本图像属性》 linestyle / ls 连接的线条样式:'-' or 'solid', '--' or 'dashed', '-.' or 'dashdot' ':' or 'dotted', 'none' or ' ' or '' linewidth / lw 连接的线条宽度,float 类型,默认 0.8 marker 标记样式,具体样式参见表二 markeredgecolor / mec marker 标记的边缘颜色 markeredgewidth / mew marker 标记的边缘宽度 markerfacecolor / mfc marker 标记的颜色 markerfacecoloralt / mfcalt marker 标记的备用颜色 markersize / ms marker 标记的大小 表二:marker 标记的样式,官方文档:https://matplotlib.org/api/markers_api.html 标记 描述 "." 点 "," 像素点 "o" 圆圈 "v" 倒三角 "^" 正三角 "<" 左三角 ">" 右三角 "1" 倒三叉星 "2" 正三叉星(类似奔驰车标形状) "3" 左三叉星 "4" 右三叉星 "8" 八边形 "s" 正方形 "p" 五边形 "P" 填充的加号(粗加号) "+" 加号 "*" 星形 "h" 六边形(底部是角) "H" 六边形(底部是边) "x" x 号 "X" 填充的 x 号(粗 x 号) "D" 粗菱形(对角线相等) "d" 细菱形(对角线不等) `” “` 垂直线 "_" 水平线 0 水平线靠左 1 水平线靠右 2 垂直线靠上 3 垂直线靠下 4 左三角(比 "<" 更细) 5 右三角(比 ">" 更细) 6 正三角(比 "^" 更细) 7 倒三角(比 "v" 更细) 8 左三角(比 "<" 更细,靠左显示) 9 右三角(比 ">" 更细,靠右显示) 10 正三角(比 "^" 更细,靠上显示) 11 倒三角(比 "v" 更细,靠下显示) "None" / " " / "" 无样式 '$...$' 支持 LaTeX 数学公式,表达式用美元符号包围起来 【3x00】绘制极坐标1234567891011121314151617181920212223242526272829import numpy as npimport matplotlib.pyplot as plt# 设置中文显示plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']# 设置画布大小plt.figure(figsize=(8.0, 6.0))# 设置三个数据,theta 为点位置的弧度参数,r 为点的半径坐标theta1 = np.array([1.25*np.pi, np.pi/2, 0])theta2 = np.array([-np.pi/6, -np.pi/2, 0, np.pi/2, np.pi])theta3 = np.arange(0., 2*np.pi, 0.5)r1 = np.array([4, 2, 3])r2 = np.array([5, 2, 4, 5, 3])r3 = np.random.randint(0, 5, 13)# 绘制第一个极坐标图,点的标记样式为细菱形,大小为8,点之间的连接线条样式为:plt.polar(theta1, r1, marker='d', ms=8, ls=':', label='数据一')# 填充第一个极坐标图,填充颜色为蓝色,透明度0.3plt.fill(theta1, r1, color='b', alpha=0.3)# 绘制第二个极坐标图,marker、linestyle、color 三个参数可以组合以字符串形式传入plt.polar(theta2, r2, '*-g', ms=10, label='数据二')# 绘制第三个极坐标图,设置 linestyle 为 none,即点与点之间不相连plt.polar(theta3, r3, marker='o', ls='none', ms=8, color='r', label='数据三')plt.title('matplotlib.pyplot.polar 用法示例', pad=25, fontsize=15)plt.legend(bbox_to_anchor=(1.3, 1))plt.show() 示例中 figure、title、legend 等其他方法的解释可参见我的系列文章: 《Python 数据分析三剑客之 Matplotlib(一):初识 Matplotlib 与其 matplotibrc 配置文件》 《Python 数据分析三剑客之 Matplotlib(二):文本描述 / 中文支持 / 画布 / 网格等基本图像属性》 《Python 数据分析三剑客之 Matplotlib(三):图例 / LaTeX / 刻度 / 子图 / 补丁等基本图像属性》 绘制结果如下图: 【4x00】绘制雷达图雷达图是以从同一点开始的轴上表示的三个或更多个定量变量的二维图表的形式显示多变量数据的图形方法。轴的相对位置和角度通常是无信息的。 雷达图也称为网络图,蜘蛛图,星图,蜘蛛网图,不规则多边形,极坐标图或 Kiviat 图。它相当于平行坐标图,轴径向排列。 在前面的示例中,使用了 matplotlib.pyplot.fill() 方法对三个极坐标点围成的图形进行了填充,这就有点儿接近于雷达图了,仔细观察前面的示例,在填充时第一个点和最后一个点之间没有连线,即没有闭合,而更精确的雷达图应该是闭合的,且外围应该是文字描述而不是度数。 在绘制雷达图之前需要提前了解一些函数。这些函数可以帮助我们实现闭合、自定义文字标签等。 【4x01】理解 numpy.concatenate()numpy.concatenate() 方法用于沿现有轴连接一系列数组,我们可以利用此方法来实现闭合操作。 基本语法:numpy.concatenate((a1, a2, ...)[, axis=0, out=None]) 参数 描述 a1, a2, … 要连接的数组,必须拥有相同的维度 axis 沿指定轴连接数组,可选项,如果 axis 为 None,则数组在使用前被展平,默认值为 0 out 用于接收连接后的数组,可选项 用法示例: 12345import numpy as npa = np.array([1, 2, 3, 4])b = np.array(['a', 'b', 'c', 'd'])print(np.concatenate((a, b))) 输出结果如下: 1['1' '2' '3' '4' 'a' 'b' 'c' 'd'] 如果要实现数组的闭合,则可以传入原数组和一个新数组,其中新数组中的元素为原数组中的第一个元素,示例如下: 1234import numpy as npa = np.array([1, 2, 3, 4])print(np.concatenate((a, [a[0]]))) 输出结果如下: 1[1 2 3 4 1] 【4x02】理解 pyplot.thetagrids()matplotlib.pyplot.thetagrids() 方法用于获取并设置当前极区图上的极轴。 基本语法:matplotlib.pyplot.thetagrids(angles, labels=None, fmt=None, **kwargs) 参数 描述 angles 网格线的角度,浮点数、度数组成的元组 labels 每个极轴要使用的文本标签,字符串组成的元组 fmt 格式化 angles 参数,如 '%1.2f' 保留两位小数,注意,将使用以弧度为单位的角度 **kwargs 其他关键字参数,参见官方文档 应用举例: 1234567891011 import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']plt.polar()angles = range(0, 360, 45)labels = ('东', '东北', '北', '西北', '西', '西南', '南', '东南')plt.thetagrids(angles, labels)plt.title('matplotlib.pyplot.thetagrids() 用法示例', pad=15)plt.show() 【4x03】绘制雷达图numpy.concatenate() 方法能够解决闭合问题,matplotlib.pyplot.thetagrids() 能够解决自定义极轴和极轴的文本标记问题,因此就可以绘制一个标准的雷达图了。示例如下: 1234567891011121314151617181920212223242526272829303132import numpy as npimport matplotlib.pyplot as plt# 设置中文显示、画布大小plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']plt.figure(figsize=(8.0, 6.0))# 分割圆并执行闭合操作(0-2π之间返回间隔均匀的6个弧度:π/3、2π/3、π、4π/3、5π/3、2π)theta = np.linspace(0, 2*np.pi, 6, endpoint=False)theta = np.concatenate((theta, [theta[0]]))# 设置两组数据并执行闭合操作data1 = np.array([9, 4, 3, 5, 2, 8])data2 = np.array([3, 6, 9, 6, 3, 2])data1 = np.concatenate((data1, [data1[0]]))data2 = np.concatenate((data2, [data2[0]]))# 绘制并填充两组数据plt.polar(theta, data1, 'bo-', label='小王')plt.polar(theta, data2, 'ro:', label='小张')plt.fill(theta, data1, color='b', alpha=0.3)plt.fill(theta, data2, color='r', alpha=0.3)# 将六个弧度(π/3、2π/3、π、4π/3、5π/3、2π)转换成角度,并分别设置标签labels = np.array(['Python', 'Golang', 'Java', 'C++', 'PHP', 'JavaScript'])plt.thetagrids(theta * 180/np.pi, labels)# 设置刻度范围、标题、图例plt.ylim(0, 10)plt.title('编程语言掌握程度')plt.legend(bbox_to_anchor=(1.3, 1))plt.show() 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106162412未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【5x00】高级用法:绘制极坐标散点图matplotlib.pyplot.polar() 方法可以实现极坐标散点图,但仅用这一个函数的话实现的样式效果并不多,以下介绍另外三种绘制极坐标散点图的方法: matplotlib.pyplot.polar() 和 matplotlib.pyplot.scatter() 结合,前者绘制极坐标图,后者在极坐标图上绘制散点图; matplotlib.pyplot.subplot() 和 matplotlib.pyplot.scatter() 结合,前者添加子图,其中指定 projection='polar' 即为极坐标图, 后者在极坐标图上绘制散点图; matplotlib.pyplot.axes() 与 matplotlib.pyplot.scatter() 结合,前者设置绘图区参数,其中指定 projection='polar' 或 polar=True 即为极坐标图, 后者在极坐标图上绘制散点图。 【5x01】方法一:pyplot.scatter() 与 pyplot.polar()以下用到的 matplotlib.pyplot.scatter() 函数,各参数含义以及支持的其他参数可以参见前文: 《Python 数据分析三剑客之 Matplotlib(二):文本描述 / 中文支持 / 画布 / 网格等基本图像属性》 《Python 数据分析三剑客之 Matplotlib(五):散点图的绘制》 12345678910111213141516import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']N = 50r = 2 * np.random.rand(N)theta = 2 * np.pi * np.random.rand(N)size = 200 * r ** 2colors = N * np.random.rand(N)plt.polar()plt.scatter(theta, r, s=size, c=colors, alpha=0.8)plt.title('极坐标散点图示例一', pad=15)plt.show() 【5x02】方法二:pyplot.scatter() 与 pyplot.subplot()matplotlib.pyplot.subplot() 方法用于添加子图,如果想要子图为极坐标图,则需要指定 projection 参数为 polar,有关此函数的具体介绍可参见官方文档。其他函数的参数解释可参考前文: 《Python 数据分析三剑客之 Matplotlib(三):图例 / LaTeX / 刻度 / 子图 / 补丁等基本图像属性》 《Python 数据分析三剑客之 Matplotlib(五):散点图的绘制》 1234567891011121314151617import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']N = 50r = 2 * np.random.rand(N)theta = 2 * np.pi * np.random.rand(N)size = 200 * r ** 2colors = N * np.random.rand(N)# 一行一列第一个子图plt.subplot(111, projection='polar')plt.scatter(theta, r, s=size, c=colors, alpha=0.8)plt.title('极坐标散点图示例二', pad=15)plt.show() 【5x03】方法三:pyplot.scatter() 与 pyplot.axes()axes 为 Matplotlib 图像中的绘图区,matplotlib.pyplot.axes() 方法可以对绘图区进行设置,同样的也可以设置 projection 参数为 polar 来实现极坐标图,设置 polar=True 也行。示例中其他函数的参数解释可参考前文: 《Python 数据分析三剑客之 Matplotlib(一):初识 Matplotlib 与其 matplotibrc 配置文件》 《Python 数据分析三剑客之 Matplotlib(五):散点图的绘制》 1234567891011121314151617import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']N = 50r = 2 * np.random.rand(N)theta = 2 * np.pi * np.random.rand(N)size = 200 * r ** 2colors = N * np.random.rand(N)# plt.axes(polar=True)plt.axes(projection='polar')plt.scatter(theta, r, s=size, c=colors, alpha=0.8)plt.title('极坐标散点图示例三', pad=15)plt.show() 【6x00】高级用法:绘制极坐标柱状图和极坐标散点图的绘制类似,matplotlib.pyplot.polar() 方法可以实现极坐标图,但仅用这一个函数的话实现的样式效果并不多,以下介绍另外三种绘制极坐标柱状图的方法: matplotlib.pyplot.polar() 和 matplotlib.pyplot.bar() 结合,前者绘制极坐标图,后者在极坐标图上绘制柱状图; matplotlib.pyplot.subplot() 和 matplotlib.pyplot.bar() 结合,前者添加子图,其中指定 projection='polar' 即为极坐标图, 后者在极坐标图上绘制柱状图; matplotlib.pyplot.axes() 与 matplotlib.pyplot.bar() 结合,前者设置绘图区参数,其中指定 projection='polar' 或 polar=True 即为极坐标图, 后者在极坐标图上绘制柱状图。 【6x01】方法一:pyplot.bar() 与 pyplot.polar()以下用到的 matplotlib.pyplot.bar() 函数,各参数含义以及支持的其他参数可以参见前文: 《Python 数据分析三剑客之 Matplotlib(二):文本描述 / 中文支持 / 画布 / 网格等基本图像属性》 《Python 数据分析三剑客之 Matplotlib(六):直方图 / 柱状图 / 条形图的绘制》 1234567891011121314import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']r = np.random.rand(8)theta = np.arange(0, 2 * np.pi, 2 * np.pi / 8)colors = np.array(['#4bb2c5', '#c5b47f', '#EAA228', '#579575', '#839557', '#958c12', '#953579', '#4b5de4'])plt.polar()plt.bar(theta, r, color=colors, alpha=0.8)plt.title('极坐标柱状图示例一', pad=15)plt.show() 【6x02】方法二:pyplot.bar() 与 pyplot.subplot()matplotlib.pyplot.subplot() 方法用于添加子图,如果想要子图为极坐标图,则需要指定 projection 参数为 polar,有关此函数的具体介绍可参见官方文档。其他函数的参数解释可参考前文: 《Python 数据分析三剑客之 Matplotlib(三):图例 / LaTeX / 刻度 / 子图 / 补丁等基本图像属性》 《Python 数据分析三剑客之 Matplotlib(六):直方图 / 柱状图 / 条形图的绘制》 1234567891011121314import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']r = np.random.rand(8)theta = np.arange(0, 2 * np.pi, 2 * np.pi / 8)colors = np.array(['#4bb2c5', '#c5b47f', '#EAA228', '#579575', '#839557', '#958c12', '#953579', '#4b5de4'])plt.subplot(111, projection='polar')plt.bar(theta, r, color=colors, alpha=0.8)plt.title('极坐标柱状图示例二', pad=15)plt.show() 【6x03】方法三:pyplot.bar() 与 pyplot.axes()axes 为 Matplotlib 图像中的绘图区,matplotlib.pyplot.axes() 方法可以对绘图区进行设置,同样的也可以设置 projection 参数为 polar 来实现极坐标图,设置 polar=True 也行。示例中其他函数的参数解释可参考前文: 《Python 数据分析三剑客之 Matplotlib(一):初识 Matplotlib 与其 matplotibrc 配置文件》 《Python 数据分析三剑客之 Matplotlib(六):直方图 / 柱状图 / 条形图的绘制》 123456789101112131415import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']r = np.random.rand(8)theta = np.arange(0, 2 * np.pi, 2 * np.pi / 8)colors = np.array(['#4bb2c5', '#c5b47f', '#EAA228', '#579575', '#839557', '#958c12', '#953579', '#4b5de4'])# plt.axes(polar=True)plt.axes(projection='polar')plt.bar(theta, r, color=colors, alpha=0.8)plt.title('极坐标柱状图示例三', pad=15)plt.show() 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106162412未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Matplotlib","slug":"Python-数据分析/Matplotlib","permalink":"https://www.itrhx.com/categories/Python-数据分析/Matplotlib/"}],"tags":[{"name":"Matplotlib","slug":"Matplotlib","permalink":"https://www.itrhx.com/tags/Matplotlib/"},{"name":"极区图","slug":"极区图","permalink":"https://www.itrhx.com/tags/极区图/"},{"name":"极坐标图","slug":"极坐标图","permalink":"https://www.itrhx.com/tags/极坐标图/"},{"name":"雷达图","slug":"雷达图","permalink":"https://www.itrhx.com/tags/雷达图/"}]},{"title":"Python 数据分析三剑客之 Matplotlib(八):等高线/等值线图的绘制","slug":"A75-Matplotlib-08","date":"2020-04-29T17:28:26.784Z","updated":"2020-08-06T03:19:41.341Z","comments":true,"path":"2020/04/30/A75-Matplotlib-08/","link":"","permalink":"https://www.itrhx.com/2020/04/30/A75-Matplotlib-08/","excerpt":"","text":"Matplotlib 系列文章: Python 数据分析三剑客之 Matplotlib(一):初识 Matplotlib 与其 matplotibrc 配置文件 Python 数据分析三剑客之 Matplotlib(二):文本描述 / 中文支持 / 画布 / 网格等基本图像属性 Python 数据分析三剑客之 Matplotlib(三):图例 / LaTeX / 刻度 / 子图 / 补丁等基本图像属性 Python 数据分析三剑客之 Matplotlib(四):线性图的绘制 Python 数据分析三剑客之 Matplotlib(五):散点图的绘制 Python 数据分析三剑客之 Matplotlib(六):直方图 / 柱状图 / 条形图的绘制 Python 数据分析三剑客之 Matplotlib(七):饼状图的绘制 Python 数据分析三剑客之 Matplotlib(八):等高线 / 等值线图的绘制 Python 数据分析三剑客之 Matplotlib(九):极区图 / 极坐标图 / 雷达图的绘制 Python 数据分析三剑客之 Matplotlib(十):3D 图的绘制 Python 数据分析三剑客之 Matplotlib(十一):最热门最常用的 50 个图表【译文】 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106066852未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【1x00】等高线概念参考百度百科,等高线概念总结如下:等高线指的是地形图上高程相等的相邻各点所连成的闭合曲线。把地面上海拔高度相同的点连成的闭合曲线,并垂直投影到一个水平面上,并按比例缩绘在图纸上,就得到等高线。等高线也可以看作是不同海拔高度的水平面与实际地面的交线,所以等高线是闭合曲线。在等高线上标注的数字为该等高线的海拔。 位于同一等高线上的地面点,海拔高度相同。但海拔高度相同的点不一定位于同一条等高线上; 在同一幅图内,除了陡崖以外,不同高程的等高线不能相交; 在图廓内相邻等高线的高差一般是相同的,因此地面坡度与等高线之间的等高线平距成反比,等高线平距愈小,等高线排列越密,说明地面坡度越大;等高线平距愈大,等高线排列越稀,则说明地面坡度愈小; 等高线是一条闭合的曲线,如果不能在同一幅内闭合,则必在相邻或者其他图幅内闭合。 等高线经过山脊或山谷时改变方向,因此,山脊线或者山谷线应垂直于等高线转折点处的切线,即等高线与山脊线或者山谷线正交。 在 Matplotlib 等高线的绘制中,需要传递三个基本参数:某个点的 x、y 轴坐标以及其高度。 【2x00】理解 numpy.meshgrid()numpy.meshgrid() 方法用于生成网格点坐标矩阵。 123456import numpy as npa = np.array([1, 2, 3])b = np.array([7, 8, 9])res = np.meshgrid(a, b)print(res) 输出结果: 123456[array([[1, 2, 3], [1, 2, 3], [1, 2, 3]]), array([[7, 7, 7], [8, 8, 8], [9, 9, 9]])] 给定两个数组,a[1, 2, 3] 和 b[7, 8, 9],a 作为 x 轴数据,b 作为 y 轴数据,那么一共可以绘制出 9 个点: (1,7)、(1,8)、(1,9)、(2,7)、(2,8)、(2,9)、(3,7)、(3,8)、(3,9),而 numpy.meshgrid() 方法就是起这样的作用,返回的两个二维数组,横坐标矩阵 a 中的每个元素,与纵坐标矩阵 b 中对应位置元素,共同构成一个点的完整坐标。 因为在 matplotlib.pyplot.contour() 等高线绘制函数中接收的是二维坐标信息,所以在绘制等高线图之前要将原数据经过 numpy.meshgrid() 方法处理,也可以自己构建类似于上述的二维数组。 【3x00】绘制方法 matplotlib.pyplot.contour()matplotlib.pyplot.contour() 方法可用于绘制等高线图。 基本语法:matplotlib.pyplot.contour(\\*args, data=None, \\*\\*kwargs) 通用格式:matplotlib.pyplot.contour([X, Y,] Z, [levels], **kwargs) 基本参数: 参数 描述 X, Y 数组形式的点的 x 和 y 轴坐标,两者都必须是二维的,形状与 Z 相同 Z 绘制轮廓的高度值,二维数组,每个元素是其对应点的高度 levels 确定等高线的数目和位置,如果是整数 N,则使用 N 个数据间隔,即绘制 N+1 条等高线如果是数组形式,则绘制指定的等高线。值必须按递增顺序排列 其他参数: 参数 描述 colors 等高线的颜色,颜色字符串或颜色序列 cmap 等高线的颜色,字符串或者 Colormap通常包含一系列的渐变色或其他颜色组合,取值参见【6x00】Colormap 取值 alpha 透明度,介于0(透明)和1(不透明)之间 origin 通过指定 Z[0,0] 的位置来确定 Z 的方向和确切位置,仅当未指定 X, Y 时才有意义None:Z[0,0] 位于左下角的 X=0, Y=0 处'lower':Z [0, 0] 位于左下角的 X = 0.5, Y = 0.5 处'upper':Z[0,0] 位于左上角的 X=N+0.5, Y=0.5 处'image':使用 rcParams[“image.origin”] = 'upper'的值 antialiased 是否启用抗锯齿渲染,默认 True linewidths 等高线的线宽,如果是数字,则所有等高线都将使用此线宽如果是序列,则将按指定的顺序以升序打印线宽默认为 rcParams[“lines.linewidth”] = 1.5 linestyles 等高线的样式,如果线条颜色为单色,则负等高线默认为虚线'-' or 'solid', '--' or 'dashed', '-.' or 'dashdot' ':' or 'dotted', 'none' or ' ' or '' 【4x00】填充方法 matplotlib.pyplot.contourf()matplotlib.pyplot.contourf() 方法与 matplotlib.pyplot.contour() 的区别在于:contourf() 会对等高线间的区域进行颜色填充(filled contours)。除此之外两者的函数签名和返回值都相同。 基本语法:matplotlib.pyplot.contourf(\\*args, data=None, \\*\\*kwargs) 通用格式:matplotlib.pyplot.contour([X, Y,] Z, [levels], **kwargs) 基本参数: 参数 描述 X, Y 数组形式的点的 x 和 y 轴坐标,两者都必须是二维的,形状与 Z 相同 Z 绘制轮廓的高度值,二维数组,每个元素是其对应点的高度 levels 确定等高线的数目和位置,如果是整数 N,则使用 N 个数据间隔,即绘制 N+1 条等高线如果是数组形式,则绘制指定的等高线。值必须按递增顺序排列 其他参数: 参数 描述 colors 等高线的填充颜色,颜色字符串或颜色序列 cmap 等高线的填充颜色,字符串或者 Colormap通常包含一系列的渐变色或其他颜色组合,取值参见【6x00】Colormap 取值 alpha 透明度,介于0(透明)和1(不透明)之间 origin 通过指定 Z[0,0] 的位置来确定 Z 的方向和确切位置,仅当未指定 X, Y 时才有意义None:Z[0,0] 位于左下角的 X=0, Y=0 处'lower':Z [0, 0] 位于左下角的 X = 0.5, Y = 0.5 处'upper':Z[0,0] 位于左上角的 X=N+0.5, Y=0.5 处'image':使用 rcParams[“image.origin”] = 'upper'的值 antialiased 是否启用抗锯齿渲染,默认 True linewidths 等高线的线宽,如果是数字,则所有等高线都将使用此线宽如果是序列,则将按指定的顺序以升序打印线宽默认为 rcParams[“lines.linewidth”] = 1.5 linestyles 等高线的样式,如果线条颜色为单色,则负等高线默认为虚线'-' or 'solid', '--' or 'dashed', '-.' or 'dashdot' ':' or 'dotted', 'none' or ' ' or '' 【5x00】标记方法 matplotlib.pyplot.clabel()matplotlib.pyplot.clabel(CS, \\*args, \\*\\*kwargs) 方法可用于标记等高线图。 参数 描述 CS ContourSet(等高线集)对象,即 pyplot.contour() 返回的对象 levels 需要标记的等高线集,数组类型,如果未指定则默认标记所有等高线 fontsize 标记的字体大小,可选项:'xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large' colors 标记的颜色,颜色字符串或颜色序列 inline 是否在标签位置移除轮廓显示,bool 类型,默认 True inline_spacing 标签位置移除轮廓的宽度,float 类型,默认为 5 fmt 标签的格式字符串。str 或 dict 类型,默认值为 %1.3f rightside_up 是否将标签旋转始终与水平面成正负90度,bool 类型,默认 True use_clabeltext 默认为 False,如果为 True,则使用 ClabelText 类(而不是 Text)创建标签ClabelText 在绘图期间重新计算文本的旋转角度,如果轴的角度发生变化,则可以使用此功能 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106066852未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【6x00】Colormap 取值matplotlib.pyplot.contour() 和 matplotlib.pyplot.contourf() 中 cmap 参数用于设置等高线的颜色,取值通常为 Colormap 中的值,通常包含一系列的渐变色或其他颜色组合。具体参加下图。 官方文档:https://matplotlib.org/tutorials/colors/colormaps.html 【7x00】简单示例12345678910111213141516171819202122import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = np.arange(-2.0, 2.0, 0.01)y = np.arange(-2.0, 2.0, 0.01)m, n = np.meshgrid(x, y) # 生成网格点坐标矩阵# 指定一个函数用于计算每个点的高度,也可以直接使用二维数组储存每个点的高度def f(a, b): return (1 - b ** 5 + a ** 5) * np.exp(-a ** 2 - b ** 2)# 绘制等高线图,8 个数据间隔,颜色为黑色plt.contour(m, n, f(m, n), 8, colors='k')plt.title('等高线图简单示例')plt.xlabel('x axis label')plt.ylabel('y axis label')plt.show() 【8x00】添加标记matplotlib.pyplot.clabel() 方法用于给等高线添加标记。 123456789101112131415161718192021222324import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = np.arange(-2.0, 2.0, 0.01)y = np.arange(-2.0, 2.0, 0.01)m, n = np.meshgrid(x, y) # 生成网格点坐标矩阵# 指定一个函数用于计算每个点的高度,也可以直接使用二维数组储存每个点的高度def f(a, b): return (1 - b ** 5 + a ** 5) * np.exp(-a ** 2 - b ** 2)# 绘制等高线图,8 个数据间隔,颜色为黑色C = plt.contour(m, n, f(m, n), 8, colors='k')# 添加标记,标记处不显示轮廓线,颜色为黑红绿蓝四种,保留两位小数plt.clabel(C, inline=True, colors=['k', 'r', 'g', 'b'], fmt='%1.2f')plt.title('等高线图添加标记示例')plt.xlabel('x axis label')plt.ylabel('y axis label')plt.show() 【9x00】轮廓线颜色和样式matplotlib.pyplot.contour() 方法中,colors 参数即可为等高线轮廓设置颜色,可以是单色,也可以是一个颜色列表,linestyles 参数可以设置轮廓线样式,注意,如果线条颜色为单色,则负等高线(高度值为负)默认为虚线。 12345678910111213141516171819202122232425import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = np.arange(-2.0, 2.0, 0.01)y = np.arange(-2.0, 2.0, 0.01)m, n = np.meshgrid(x, y) # 生成网格点坐标矩阵# 指定一个函数用于计算每个点的高度,也可以直接使用二维数组储存每个点的高度def f(a, b): return (1 - b ** 5 + a ** 5) * np.exp(-a ** 2 - b ** 2)colors = ['k', 'r', 'g', 'b']# 绘制等高线图,8 个数据间隔,颜色为黑色,线条样式为 --C = plt.contour(m, n, f(m, n), 8, colors=colors, linestyles='--')# 添加标记,标记处不显示轮廓线,颜色为黑红绿蓝四种,保留两位小数plt.clabel(C, inline=True, colors=colors, fmt='%1.2f')plt.title('等高线图设置颜色/样式示例')plt.xlabel('x axis label')plt.ylabel('y axis label')plt.show() 如果想启用渐变色,则可以设置 cmap,取值参见【6x00】Colormap 取值,colorbar() 方法可以显示颜色对照条。 1234567891011121314151617181920212223242526import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = np.arange(-2.0, 2.0, 0.01)y = np.arange(-2.0, 2.0, 0.01)m, n = np.meshgrid(x, y) # 生成网格点坐标矩阵# 指定一个函数用于计算每个点的高度,也可以直接使用二维数组储存每个点的高度def f(a, b): return (1 - b ** 5 + a ** 5) * np.exp(-a ** 2 - b ** 2)# 绘制等高线图,8 个数据间隔,颜色为 plasmaC = plt.contour(m, n, f(m, n), 8, cmap='plasma')# 添加标记,标记处不显示轮廓线,颜色为黑色,保留两位小数plt.clabel(C, inline=True, colors='k', fmt='%1.2f')# 显示颜色条plt.colorbar()plt.title('等高线图设置渐变色示例')plt.xlabel('x axis label')plt.ylabel('y axis label')plt.show() 【10x00】颜色填充matplotlib.pyplot.contourf() 方法用于对等高线之间的地方进行颜色填充。 123456789101112131415161718192021222324252627import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = np.arange(-2.0, 2.0, 0.01)y = np.arange(-2.0, 2.0, 0.01)m, n = np.meshgrid(x, y) # 生成网格点坐标矩阵# 指定一个函数用于计算每个点的高度,也可以直接使用二维数组储存每个点的高度def f(a, b): return (1 - b ** 5 + a ** 5) * np.exp(-a ** 2 - b ** 2)# 绘制等高线图,8 个数据间隔,颜色为 plasmaplt.contourf(m, n, f(m, n), 8, cmap='plasma')C = plt.contour(m, n, f(m, n), 8, cmap='plasma')# 添加标记,标记处不显示轮廓线,颜色为黑色,保留两位小数plt.clabel(C, inline=True, colors='k', fmt='%1.2f')# 显示颜色条plt.colorbar()plt.title('等高线图颜色填充示例')plt.xlabel('x axis label')plt.ylabel('y axis label')plt.show() 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106066852未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Matplotlib","slug":"Python-数据分析/Matplotlib","permalink":"https://www.itrhx.com/categories/Python-数据分析/Matplotlib/"}],"tags":[{"name":"Matplotlib","slug":"Matplotlib","permalink":"https://www.itrhx.com/tags/Matplotlib/"},{"name":"等高线图","slug":"等高线图","permalink":"https://www.itrhx.com/tags/等高线图/"},{"name":"等值线图","slug":"等值线图","permalink":"https://www.itrhx.com/tags/等值线图/"}]},{"title":"Python 数据分析三剑客之 Matplotlib(七):饼状图的绘制","slug":"A74-Matplotlib-07","date":"2020-04-23T17:14:19.796Z","updated":"2020-08-06T03:18:07.852Z","comments":true,"path":"2020/04/24/A74-Matplotlib-07/","link":"","permalink":"https://www.itrhx.com/2020/04/24/A74-Matplotlib-07/","excerpt":"","text":"Matplotlib 系列文章: Python 数据分析三剑客之 Matplotlib(一):初识 Matplotlib 与其 matplotibrc 配置文件 Python 数据分析三剑客之 Matplotlib(二):文本描述 / 中文支持 / 画布 / 网格等基本图像属性 Python 数据分析三剑客之 Matplotlib(三):图例 / LaTeX / 刻度 / 子图 / 补丁等基本图像属性 Python 数据分析三剑客之 Matplotlib(四):线性图的绘制 Python 数据分析三剑客之 Matplotlib(五):散点图的绘制 Python 数据分析三剑客之 Matplotlib(六):直方图 / 柱状图 / 条形图的绘制 Python 数据分析三剑客之 Matplotlib(七):饼状图的绘制 Python 数据分析三剑客之 Matplotlib(八):等高线 / 等值线图的绘制 Python 数据分析三剑客之 Matplotlib(九):极区图 / 极坐标图 / 雷达图的绘制 Python 数据分析三剑客之 Matplotlib(十):3D 图的绘制 Python 数据分析三剑客之 Matplotlib(十一):最热门最常用的 50 个图表【译文】 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106025845未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【1x00】方法描述matplotlib.pyplot.pie() 方法用于绘制饼状图。 基本语法: 1234567matplotlib.pyplot.pie( x[, explode=None, labels=None, colors=None, autopct=None, pctdistance=0.6, shadow=False, labeldistance=1.1, startangle=None, radius=None, counterclock=True, wedgeprops=None, textprops=None, center=(0, 0), frame=False, rotatelabels=False, \\*, data=None] ) 参数 描述 x 每个扇形块的大小,数组形式,大小单位是比例 explode 指定对应扇形块脱离饼图的半径大小,数组形式,其中元素个数应该是 len(x) labels 每个扇形块上的文本标签,列表形式 labeldistance 每个扇形块上的文本标签与扇形中心的距离,float 类型,默认 1.1 colors 每个扇形块对应的颜色,数组形式 autopct 用于计算每个扇形块所占比例,字符串或者函数类型例如:autopct='%1.1f%%' 表示浮点数,保留一位小数,并添加百分比符号 pctdistance 每个扇形块的中心与 autopct 生成的文本之间的距离,float 类型,默认 0.6 shadow 是否为扇形添加阴影效果 startangle 将饼图按照逆时针旋转指定的角度,float 类型 radius 饼图的半径,如果是 None,则将被设置为 1,float 类型 counterclock 是否按照逆时针对扇形图进行排列,bool 类型,默认 True wedgeprops 传递给绘制每个扇形图对象的参数,字典形式,参数值参见 Wedge例如:wedgeprops = {'linewidth': 3} 设置扇形边框线宽度为 3 textprops 传递给文本对象的参数,字典形式例如:textprops={'color': 'r', 'fontsize': 15} 设置文字为红色,大小为15 center 饼图圆心在画布上是坐标,默认 (0, 0) frame 是否显示 x, y 坐标轴外框,默认 False rotatelabels 是否按照角度进行调整每块饼的 label 文本标签,默认 False 【2x00】简单示例12345678910111213import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = [10, 30, 45, 15]labels = ['Java', 'Golang', 'Python', 'C++']colors = ['red', 'yellow', 'blue', 'green']# 指定4个扇区所占比例以及扇区的颜色,扇区文本标签距离扇区中心1.1plt.pie(x, labels=labels, colors=colors, labeldistance=1.1)plt.title('饼状图简单示例')plt.show() 【3x00】按角度调整扇形标签rotatelabels 属性可以设置是否按照角度调整每块饼的 label(标签)显示方式。 12345678910111213import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = [10, 30, 45, 15]labels = ['Java', 'Go', 'Python', 'C++']colors = ['red', 'yellow', 'blue', 'green']# 指定4个扇区所占比例以及扇区的颜色,扇区文本标签距离扇区中心1.1,按角度调整 labelsplt.pie(x, labels=labels, colors=colors, labeldistance=1.1, rotatelabels=True)plt.title('饼状图按角度调整 labels 示例')plt.show() 【4x00】显示图例与前面文章中绘制线性图、散点图、条形图一样,调用 matplotlib.pyplot.legend() 方法可绘制图例,该方法的参数解释参见前文《Python 数据分析三剑客之 Matplotlib(三):图例 / LaTeX / 刻度 / 子图 / 补丁等基本图像属性》 12345678910111213import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = [10, 30, 45, 15]labels = ['Java', 'Go', 'Python', 'C++']colors = ['red', 'yellow', 'blue', 'green']plt.pie(x, labels=labels, colors=colors, labeldistance=1.1)plt.title('饼状图显示图例示例')plt.legend(bbox_to_anchor=(1, 1))plt.show() 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106025845未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【5x00】突出显示扇形块explode 参数可以实现突出显示某一块扇区,接收数组形式的参数,这个数组中的元素个数应该是 len(x),即和扇区块的数量相同。 1234567891011121314import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = [10, 30, 45, 15]labels = ['Java', 'Golang', 'Python', 'C++']colors = ['red', 'yellow', 'blue', 'green']# 指定第一个扇区块脱离饼图的半径大小为0.3,其它扇区不脱离plt.pie(x, labels=labels, colors=colors, labeldistance=1.1, explode=[0.3, 0, 0, 0])plt.title('饼状图突出显示扇形块示例')plt.legend(bbox_to_anchor=(1, 1))plt.show() 【6x00】显示各扇区所占百分比autopct 参数可用于计算每个扇形块所占比例,接收字符串或者函数类型,例如:autopct='%1.1f%%' 表示浮点数,保留一位小数,并添加百分比符号。pctdistance 参数用于调整每个扇形块的中心与 autopct 生成的文本之间的距离,float 类型,默认 0.6。 123456789101112131415161718192021import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = [10, 30, 45, 15]labels = ['Java', 'Golang', 'Python', 'C++']colors = ['red', 'yellow', 'blue', 'green']plt.pie( x, # 每个扇形块所占比例 labels=labels, # 扇形块文本标签 colors=colors, # 扇形块颜色 labeldistance=1.1, # 扇形块标签距离中心的距离 explode=[0.3, 0, 0, 0], # 第一个扇形块突出显示 autopct='%1.1f%%', # 显示百分比,保留一位小数 pctdistance=0.5 # 百分比文本距离饼状图中心的距离)plt.title('饼状图显示各扇区所占百分比示例')plt.legend(bbox_to_anchor=(1, 1)) # 显示图例plt.show() 【7x00】旋转饼状图startangle 参数可以选择饼状图,改变饼状图放置的角度。注意是按照逆时针旋转。 12345678910111213141516171819202122import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = [10, 30, 45, 15]labels = ['Java', 'Golang', 'Python', 'C++']colors = ['red', 'yellow', 'blue', 'green']plt.pie( x, # 每个扇形块所占比例 labels=labels, # 扇形块文本标签 colors=colors, # 扇形块颜色 labeldistance=1.1, # 扇形块标签距离中心的距离 explode=[0.3, 0, 0, 0], # 第一个扇形块突出显示 autopct='%1.1f%%', # 显示百分比,保留一位小数 pctdistance=0.5, # 百分比文本距离饼状图中心的距离 startangle=-90 # 逆时针旋转-90°,即顺时针旋转90°)plt.title('饼状图旋转角度示例')plt.legend(bbox_to_anchor=(1, 1)) # 显示图例plt.show() 【8x00】自定义每个扇形和文字属性wedgeprops 参数以字典形式为每个扇形添加自定义属性,例如:wedgeprops = {'linewidth': 3} 设置扇形边框线宽度为 3,更多其他参数值参见 Wedge; textprops 参数同样以字典形式为文本对象添加自定义属性,例如:textprops={'color': 'r', 'fontsize': 15} 设置文字为红色,大小为15,更多其他参数值参见 Text。 1234567891011121314151617181920212223242526272829303132import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = [10, 30, 45, 15]labels = ['Java', 'Golang', 'Python', 'C++']colors = ['red', 'yellow', 'blue', 'green']plt.pie( x, # 每个扇形块所占比例 labels=labels, # 扇形块文本标签 colors=colors, # 扇形块颜色 labeldistance=1.1, # 扇形块标签距离中心的距离 explode=[0.3, 0, 0, 0], # 第一个扇形块突出显示 autopct='%1.1f%%', # 显示百分比,保留一位小数 pctdistance=0.6, # 百分比文本距离饼状图中心的距离 shadow=True, # 显示阴影效果 wedgeprops={ # 为每个扇形添加属性 'width': 0.7, # 扇形宽度0.7 'edgecolor': '#98F5FF', # 扇形边缘线颜色 'linewidth': 3 # 扇形边缘线宽度 }, textprops={ # 为文字添加属性 'fontsize': 13, # 文字大小 'fontweight': 'bold', # 文字粗细 'color': 'k' # 文字颜色,黑色 })plt.title('饼状图自定义每个扇形和文字属性示例', fontweight='bold')plt.legend(bbox_to_anchor=(1, 1), borderpad=0.6) # 显示图例plt.show() 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/106025845未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Matplotlib","slug":"Python-数据分析/Matplotlib","permalink":"https://www.itrhx.com/categories/Python-数据分析/Matplotlib/"}],"tags":[{"name":"Matplotlib","slug":"Matplotlib","permalink":"https://www.itrhx.com/tags/Matplotlib/"},{"name":"饼状图","slug":"饼状图","permalink":"https://www.itrhx.com/tags/饼状图/"}]},{"title":"Python 数据分析三剑客之 Matplotlib(六):直方图/柱状图/条形图的绘制","slug":"A73-Matplotlib-06","date":"2020-04-21T15:29:46.364Z","updated":"2020-08-06T03:16:13.213Z","comments":true,"path":"2020/04/21/A73-Matplotlib-06/","link":"","permalink":"https://www.itrhx.com/2020/04/21/A73-Matplotlib-06/","excerpt":"","text":"Matplotlib 系列文章: Python 数据分析三剑客之 Matplotlib(一):初识 Matplotlib 与其 matplotibrc 配置文件 Python 数据分析三剑客之 Matplotlib(二):文本描述 / 中文支持 / 画布 / 网格等基本图像属性 Python 数据分析三剑客之 Matplotlib(三):图例 / LaTeX / 刻度 / 子图 / 补丁等基本图像属性 Python 数据分析三剑客之 Matplotlib(四):线性图的绘制 Python 数据分析三剑客之 Matplotlib(五):散点图的绘制 Python 数据分析三剑客之 Matplotlib(六):直方图 / 柱状图 / 条形图的绘制 Python 数据分析三剑客之 Matplotlib(七):饼状图的绘制 Python 数据分析三剑客之 Matplotlib(八):等高线 / 等值线图的绘制 Python 数据分析三剑客之 Matplotlib(九):极区图 / 极坐标图 / 雷达图的绘制 Python 数据分析三剑客之 Matplotlib(十):3D 图的绘制 Python 数据分析三剑客之 Matplotlib(十一):最热门最常用的 50 个图表【译文】 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105952856未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【1x00】直方图 / 柱状图 / 条形图的区别 直方图:直方图(Histogram)又称质量分布图,是一种统计报告图,由一系列高度不等的纵向条纹或线段表示数据分布的情况。一般用于描述连续型数据的分布关系,用横轴表示数据类型,纵轴表示分布情况。直方图是用面积表示各组频数的多少,矩形的高度表示每一组的频数或频率,宽度则表示各组的组距,因此其高度与宽度均有意义。其次,由于分组数据具有连续性,直方图的各矩形通常是连续排列。 柱状图:柱状图(bar chart)又称条图、长条图、柱状统计图、条状图、棒形图,是一种以长方形的长度为变量的统计图表。一般用于描述离散型分类数据的对比,长条图用来比较两个或以上的价值(不同时间或者不同条件),只有一个变量,通常利用于较小的数据集分析。柱状图亦可横向排列,或用多维方式表达。柱状图各矩形的宽度固定,矩形之间分开排列,会有间距。 条形图:通常情况下条形图 = 柱状图,也可以将横向排列的柱状图称为条形图。在本文中会将条形图视为后者。 【2x00】直方图的绘制【2x01】函数介绍 matplotlib.pyplot.hist()matplotlib.pyplot.hist() 函数用于绘制直方图。 基本语法:matplotlib.pyplot.hist(x[, bins=None, range=None, density=False, bottom=None, histtype='bar', align='mid', orientation='vertical', rwidth=None, log=False, color=None, label=None, stacked=False, \\*\\*kwargs]) 基本参数: 参数 描述 x 数据集,数组或数组序列 bins 统计的分布区间、条形数,可以是整数、序列或字符串,默认 rcParams["hist.bins"] =10如果 bins 是整数,则定义的是等宽的矩形的个数如果 bins 是序列,则定义的是每个矩形的区间,如:bins = [1, 2, 3, 4],则矩形分布区间为 [1,2)、[2,3)、[3,4]如果 bins 是字符串,则它应该是 numpy.histogram_bin_edges 所支持的策略之一 range 矩形分布的区间,在没有指定 bins 生效,元组类型 density 是否显示频率统计结果,频率统计结果=区间数目/(总数*区间宽度) bottom y 轴的起始位置,默认为 0 histtype 矩形的样式,有四种类型可选:'bar':默认值,传统的条形直方图,如果给出多个数据,则条形图并排排列'barstacked':当数据为 1 个时,和 bar 结果一样,当数据为多个时,则进行垂直堆叠'step':未填充的线条形式;'stepfilled':填充的线条形式,效果与 bar 差不多 align 矩形的中心位于 bins(x 轴) 的位置,'left':左;'mid':中;'right':右 orientation 矩形的方向,vertical:垂直;horizontal:水平 rwidth 矩形的相对宽度,如果未指定,则自动计算宽度 log y 坐标轴是否以指数刻度显示 color 矩形的颜色,默认蓝色,与 facecolor 作用相同,指定一个即可,如果两者都指定,则取 facecolor 的值 label 数据的标签,展示图例时使用 stacked 是否为堆积状图(当两个数据相似时,堆积在一起就会把第一个数据的显示相对缩小一点) 其他参数: 参数 描述 facecolor 标量或数组类型,每个矩形的颜色,与 color 作用相同,指定一个即可,如果两者都指定,则取 facecolor 的值 edgecolor 标量或数组类型,直方图边缘线的颜色 linewidth 标量或数组类型,直方图边缘线的宽度,如果为 0,则不绘制边 alpha float 类型,矩形透明度 label 图例中显示的标签 linestyle / ls 线条样式,此处指矩形边缘线条样式'-' or 'solid', '--' or 'dashed', '-.' or 'dashdot' or ':' or 'dotted', 'none' or ' ' or '' linewidth / lw 线条宽度,此处指矩形边缘线的宽度,float 类型,默认 0.8 hatch 矩形的填充图案,可以是组合形式,如果有相同的图案,则会增加填充的密度取值可以是:'/', '\\', `’ ‘,‘-‘,‘+’,‘x’,‘o’,‘O’,‘.’,‘*’` 【2x02】简单直方图示例123456789101112131415import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei'] # 设置中文显示x = np.random.randint(0, 101, 100) # 数据集bins = np.arange(0, 101, 10) # 分布区间 [0,10)、[10,20)...[90,100]plt.hist(x, bins=bins, linewidth=0.5, edgecolor='k') # 边缘线宽0.5,颜色为黑色plt.xlim(0, 100) # x 轴刻度范围 plt.title('简单直方图示例') # 标题plt.xlabel('x axis label') # x 轴标签plt.ylabel('y axis label') # y 轴标签plt.show() 【2x03】堆积的直方图参数 stacked 决定了将两份数据进行堆积显示。注意,有可能两个数据相似(y 轴的值相似),但是堆积在一起的时候,会把第一个数据的显示相对缩小一点。 1234567891011121314151617181920import matplotlib.pyplot as pltimport numpy as npplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']hist1 = np.random.randint(0, 100, 100)hist2 = np.random.randint(0, 100, 100)x = [hist1, hist2]colors = ['orchid', 'deepskyblue']labels = ['hist1', 'hist2']bins = range(0, 101, 10)# 绘制两份数据的直方图,数据集等其他参数可以使用列表形式传递,也可以使用两次 hist 函数单独传递plt.hist(x, bins=bins, color=colors, stacked=True, label=labels)plt.title('堆积的直方图示例')plt.xlabel('x axis label')plt.ylabel('y axis label')plt.legend(loc=\"upper left\")plt.show() 【2x04】填充其他样式hatch 参数可以让直方图的矩形填充其他样式,可选值有:'/', '\\', '|', '-', '+', 'x', 'o', 'O', '.', '*'。可以是不同图案的组合形式,如果有相同的图案,则会增加填充的密度。 12345678910111213141516import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei'] # 设置中文显示x = np.random.randint(0, 101, 100) # 数据集bins = np.arange(0, 101, 10) # 分布区间 [0,10)、[10,20)...[90,100]# 矩形颜色为白色,使用 / 填充,边缘线宽0.5,颜色为黑色plt.hist(x, bins=bins, color='w', hatch='///', linewidth=0.5, edgecolor='k')plt.xlim(0, 100) # x 轴刻度范围plt.title('直方图图案填充示例') # 标题plt.xlabel('x axis label') # x 轴标签plt.ylabel('y axis label') # y 轴标签plt.show() 【3x00】柱状图的绘制【3x01】函数介绍 matplotlib.pyplot.bar()matplotlib.pyplot.bar() 函数用于绘制柱状图。 基本语法:matplotlib.pyplot.bar(x, height[, width=0.8, bottom=None, align='center', \\*\\*kwargs]) 基本参数: 参数 描述 x 标量序列,每个矩形对应的 x 轴刻度 height 标量或标量序列,每个矩形对应的高度,即 y 轴刻度 width 标量或数组类型,每个矩形的宽度,默认为 0.8 bottom 标量或数组类型,y 轴的起始位置,默认为 0 align 矩形与 x 轴刻度对齐的位置,'center':中;'edge':左边缘 其他参数: 参数 描述 color 标量或数组类型,每个矩形的颜色,与 facecolor 作用相同,指定一个即可,如果两者都指定,则取 facecolor 的值 edgecolor 标量或数组类型,柱状图边缘线的颜色 linewidth 标量或数组类型,柱状图边缘线的宽度,如果为0,则不绘制边 tick_label 标量或数组类型,柱状图 x 轴的刻度标签,默认使用数字标签 xerr / yerr 标量,指定对应标准差(添加误差线时会用到) ecolor 标量或数组类型,误差线的线条颜色,默认值为 black capsize 标量,误差线两头横线的宽度,默认为 rcParams["errorbar.capsize"] = 0.0 error_kw 字典类型,可以此字典中定义 ecolor 和 capsize,比单独指定的优先级要高 log bool 值,y 坐标轴是否以指数刻度显示 alpha float 类型,矩形透明度 label 图例中显示的标签 linestyle / ls 线条样式,此处指矩形边缘线条样式'-' or 'solid', '--' or 'dashed', '-.' or 'dashdot' or ':' or 'dotted', 'none' or ' ' or '' linewidth / lw 线条宽度,此处指矩形边缘线的宽度,float 类型,默认 0.8 hatch 矩形的填充图案,可以是组合形式,如果有相同的图案,则会增加填充的密度取值可以是:'/', '\\', `’ ‘,‘-‘,‘+’,‘x’,‘o’,‘O’,‘.’,‘*’` 【3x02】简单柱状图示例12345678910111213141516171819import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = [1, 2, 3, 4, 5]height = [5, 7, 4, 3, 1]# 设置 x 轴的标签,也可以用 plt.xticks 方法来设置tick_label = ['A', 'B', 'C', 'D', 'E']# 设置颜色序列color = ['red', 'yellow', 'peru', 'orchid', 'deepskyblue']# 绘制柱状图,边缘线宽度为1,颜色为黑色,样式为 --plt.bar(x, height, tick_label=tick_label, color=color, edgecolor='k', linewidth=1, linestyle='--')plt.title('简单柱状图示例')plt.xlabel('x axis label')plt.ylabel('y axis label')plt.show() 【3x03】添加与标准差的误差线首先定义一个列表,其中的元素是与每个值对应的标准差,ecolor 和 capsize 参数分别指定误差线的颜色和两头横线的宽度。这两个参数可以通过 error_kw 字典形式组合起来。以字典形式的组合优先级别要比单独指定高。另外,柱状图指定标准差时要用 yerr,条形图(横向排列的柱状图)指定标准差时要用 xerr。 12345678910111213141516171819202122232425262728293031import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = [1, 2, 3, 4, 5]height = [5, 7, 4, 3, 2]std = [0.5, 0.1, 1.2, 0.3, 1.0] # 标准差tick_label = ['A', 'B', 'C', 'D', 'E'] # 设置 x 轴的标签,也可以用 plt.xticks 方法来设置color = ['red', 'yellow', 'peru', 'orchid', 'deepskyblue'] # 设置颜色序列plt.bar( x, height, tick_label=tick_label, color=color, yerr=std, # 指定对应标准差 # error_kw={ # 'ecolor': 'k', # 指定误差线的颜色 # 'capsize': 6 # 指定误差线两头横线的宽度 # }, ecolor='k', capsize=6, edgecolor='k', # 指定边缘线颜色 linewidth=1 # 指定边缘线宽度)plt.title('柱状图添加误差线示例')plt.xlabel('x axis label')plt.ylabel('y axis label')plt.show() 【3x04】多序列柱状图在绘制多序列的柱状图时,只需要多次调用 matplotlib.pyplot.bar() 函数即可,指定一个较小的宽度值(偏移量),绘制不同数据时设置不同的 x 位置刻度即可。 12345678910111213141516171819202122232425import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = np.arange(5)height1 = np.array([5, 7, 4, 3, 2])height2 = np.array([2, 4, 6, 7, 3])height3 = np.array([3, 1, 7, 5, 2])# 设置宽度值(偏移量)width = 0.3# 绘制不同数据时,x 轴依次增加一个偏移量plt.bar(x, height1, width, label='bar1')plt.bar(x + width, height2, width, label='bar2')plt.bar(x + width * 2, height3, width, label='bar3')# 设置 x 轴刻度的标签plt.xticks(x + width, ['A', 'B', 'C', 'D', 'E'])plt.title('多序列柱状图示例')plt.xlabel('x axis label')plt.ylabel('y axis label')plt.legend()plt.show() 【3x05】堆积的柱状图所谓堆积图,就是将多序列数据堆积到一个矩形上显示,在柱状图中要实现堆积图,只需要改变 bottom 参数即可,bottom 参数用于设置 y 轴基线,即柱状图的底边在 y 轴上的起始刻度,第一条数据 data1 的基线可以设置为 0,即默认值,第二条数据 data2 的基线可以设置在 data1 的上方,即 bottom=data1,第三条数据 data3 的基线可以设置在 data1 + data2 的上方,即 bottom=data1+data2,以此类推。 123456789101112131415161718192021import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = np.arange(5)height1 = np.array([5, 7, 4, 3, 2])height2 = np.array([2, 4, 6, 7, 3])height3 = np.array([3, 1, 7, 5, 2])plt.bar(x, height1, label='bar1')plt.bar(x, height2, label='bar2', bottom=height1)plt.bar(x, height3, label='bar3', bottom=(height2+height1))plt.xticks(x, ['A', 'B', 'C', 'D', 'E'])plt.title('堆积的柱状图示例')plt.xlabel('x axis label')plt.ylabel('y axis label')plt.legend()plt.show() 【3x06】填充其他样式hatch 参数可以让柱状图的矩形填充其他样式,可选值有:'/', '\\', '|', '-', '+', 'x', 'o', 'O', '.', '*'。可以是不同图案的组合形式,如果有相同的图案,则会增加填充的密度。 123456789101112131415161718192021import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = np.arange(5)height1 = np.array([5, 7, 4, 3, 2])height2 = np.array([2, 4, 6, 7, 3])height3 = np.array([3, 1, 7, 5, 2])plt.bar(x, height1, label='bar1', color='w', hatch='///')plt.bar(x, height2, label='bar2', bottom=height1, color='w', hatch='xxx')plt.bar(x, height3, label='bar3', bottom=(height2+height1), color='w', hatch='|||')plt.xticks(x, ['A', 'B', 'C', 'D', 'E'])plt.title('柱状图图案填充示例')plt.xlabel('x axis label')plt.ylabel('y axis label')plt.legend()plt.show() 【3x07】添加文字描述利用 matplotlib.pyplot.text() 方法可以在柱状图每个矩形上方添加文字描述。具体参数解释可参考前面的文章:《Python 数据分析三剑客之 Matplotlib(二):文本描述 / 中文支持 / 画布 / 网格等基本图像属性》 1234567891011121314151617181920212223242526272829303132import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = np.arange(5)height1 = np.array([5, 7, 4, 3, 2])height2 = np.array([2, 4, 6, 7, 3])height3 = np.array([3, 1, 7, 5, 2])width = 0.3# 绘制不同数据时,x 轴依次增加一个偏移量plt.bar(x, height1, width, label='bar1')plt.bar(x + width, height2, width, label='bar2')plt.bar(x + width * 2, height3, width, label='bar3')# 依次添加每条数据的标签for a, b in zip(x, height1): plt.text(a, b, b, ha='center', va='bottom')for c, d in zip(x, height2): plt.text(c + width, d, d, ha='center', va='bottom')for e, f in zip(x, height3): plt.text(e + width * 2, f, f, ha='center', va='bottom')# 设置 x 轴刻度的标签plt.xticks(x + width, ['A', 'B', 'C', 'D', 'E'])plt.title('柱状图添加文字描述示例')plt.xlabel('x axis label')plt.ylabel('y axis label')plt.legend()plt.show() 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105952856未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【4x00】条形图的绘制【4x01】函数介绍 matplotlib.pyplot.barh()matplotlib.pyplot.barh() 函数用于绘制条形图(水平排列的柱状图)。 基本语法:matplotlib.pyplot.barh(y, width[, height=0.8, left=None, align='center', color, \\*\\*kwargs]) 参数 描述 y 标量或数组类型,每个矩形对应的 y 轴刻度 width 标量或数组类型,每个矩形的宽度,即 x 轴刻度 height 标量序列,每个矩形的高度,默认 0.8 left 标量序列,每个矩形的左侧 x 坐标的起始位置,默认值为 0 align 矩形的底边与 y 轴刻度对齐的位置,'center':中;'edge':底边 其他参数: 参数 描述 color 标量或数组类型,每个矩形的颜色,与 facecolor 作用相同,指定一个即可,如果两者都指定,则取 facecolor 的值 edgecolor 标量或数组类型,条形图边缘线的颜色 linewidth 标量或数组类型,条形图边缘线的宽度,如果为0,则不绘制边 tick_label 标量或数组类型,条形图 y 轴的刻度标签,默认使用数字标签 xerr / yerr 标量,指定对应标准差(添加误差线时会用到) ecolor 标量或数组类型,误差线的线条颜色,默认值为 black capsize 标量,误差线两头横线的宽度,默认为 rcParams["errorbar.capsize"] = 0.0 error_kw 字典类型,可以此字典中定义 ecolor 和 capsize,比单独指定的优先级要高 log bool 值,y 坐标轴是否以指数刻度显示 alpha float 类型,矩形透明度 label 图例中显示的标签 linestyle / ls 线条样式,此处指矩形边缘线条样式'-' or 'solid', '--' or 'dashed', '-.' or 'dashdot' or ':' or 'dotted', 'none' or ' ' or '' linewidth / lw 线条宽度,此处指矩形边缘线的宽度,float 类型,默认 0.8 hatch 矩形的填充图案,可以是组合形式,如果有相同的图案,则会增加填充的密度取值可以是:'/', '\\', `’ ‘,‘-‘,‘+’,‘x’,‘o’,‘O’,‘.’,‘*’` 【4x02】简单条形图示例12345678910111213141516import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']y = [1, 2, 3, 4, 5]width = [5, 7, 4, 3, 1]tick_label = ['A', 'B', 'C', 'D', 'E']color = ['red', 'yellow', 'peru', 'orchid', 'deepskyblue']plt.barh(y, width, tick_label=tick_label, color=color, edgecolor='k', linewidth=1, linestyle='--')plt.title('简单条形图示例')plt.xlabel('x axis label')plt.ylabel('y axis label')plt.show() 【4x03】添加与标准差的误差线与柱状图一样,首先定义一个列表,其中的元素是与每个值对应的标准差,ecolor 和 capsize 参数分别指定误差线的颜色和两头横线的宽度。这两个参数可以通过 error_kw 字典形式组合起来。以字典形式的组合优先级别要比单独指定高。另外,柱状图指定标准差时要用 yerr,条形图(横向排列的柱状图)指定标准差时要用 xerr。 12345678910111213141516171819202122232425262728293031import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']y = [1, 2, 3, 4, 5]width = [5, 7, 4, 3, 2]std = [0.5, 0.1, 1.2, 0.3, 1.0] # 标准差tick_label = ['A', 'B', 'C', 'D', 'E'] # 设置 x 轴的标签,也可以用 plt.xticks 方法来设置color = ['red', 'yellow', 'peru', 'orchid', 'deepskyblue'] # 颜色序列plt.barh( y, width, tick_label=tick_label, color=color, xerr=std, # 指定对应标准差 # error_kw={ # 'ecolor': 'k', # 指定误差线的颜色 # 'capsize': 6 # 指定误差线两头横线的宽度 # }, ecolor='k', capsize=6, edgecolor='k', # 指定边缘线颜色 linewidth=1 # 指定边缘线宽度)plt.title('条形图添加误差线示例')plt.xlabel('x axis label')plt.ylabel('y axis label')plt.show() 【4x04】多序列条形图与多序列柱状图类似,在绘制多序列的条形图时,只需要多次调用 matplotlib.pyplot.barh() 函数即可,指定一个较小的高度值(偏移量),绘制不同数据时设置不同的 y 位置刻度即可。 12345678910111213141516171819202122232425import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']y = np.arange(5)width1 = np.array([5, 7, 4, 3, 2])width2 = np.array([2, 4, 6, 7, 3])width3 = np.array([3, 1, 7, 5, 2])# 设置高度值(偏移量)height = 0.3# 绘制不同数据时,y 轴依次增加一个偏移量plt.barh(y, width1, height, label='bar1')plt.barh(y + height, width2, height, label='bar2')plt.barh(y + height * 2, width3, height, label='bar3')# 设置 y 轴刻度的标签plt.yticks(y + height, ['A', 'B', 'C', 'D', 'E'])plt.title('多序列条形图示例')plt.xlabel('x axis label')plt.ylabel('y axis label')plt.legend()plt.show() 【4x05】堆积的条形图堆积图就是将多序列数据堆积到一个矩形上显示,和堆积的柱状图类似,在条形图中要实现堆积图,只需要改变 left 参数即可,left 参数用于设置 x 轴基线,即柱状图的底边在 x 轴上的起始刻度,第一条数据 data1 的基线可以设置为 0,即默认值,第二条数据 data2 的基线可以设置在 data1 的上方,即 left=data1,第三条数据 data3 的基线可以设置在 data1 + data2 的上方,即 left=data1+data2,以此类推。 123456789101112131415161718192021import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']y = np.arange(5)width1 = np.array([5, 7, 4, 3, 2])width2 = np.array([2, 4, 6, 7, 3])width3 = np.array([3, 1, 7, 5, 2])plt.barh(y, width1, label='bar1')plt.barh(y, width2, label='bar2', left=width1)plt.barh(y, width3, label='bar3', left=(width1+width2))plt.yticks(y, ['A', 'B', 'C', 'D', 'E'])plt.title('堆积的条形图示例')plt.xlabel('x axis label')plt.ylabel('y axis label')plt.legend()plt.show() 【4x06】填充其他样式hatch 参数可以让柱状图的矩形填充其他样式,可选值有:'/', '\\', '|', '-', '+', 'x', 'o', 'O', '.', '*'。可以是不同图案的组合形式,如果有相同的图案,则会增加填充的密度。 123456789101112131415161718192021import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']y = np.arange(5)width1 = np.array([5, 7, 4, 3, 2])width2 = np.array([2, 4, 6, 7, 3])width3 = np.array([3, 1, 7, 5, 2])plt.barh(y, width1, label='bar1', color='w', hatch='///')plt.barh(y, width2, label='bar2', left=width1, color='w', hatch='xxx')plt.barh(y, width3, label='bar3', left=(width1+width2), color='w', hatch='|||')plt.yticks(y, ['A', 'B', 'C', 'D', 'E'])plt.title('条形图图案填充示例')plt.xlabel('x axis label')plt.ylabel('y axis label')plt.legend()plt.show() 【4x07】添加文字描述利用 matplotlib.pyplot.text() 方法可以在条形图每个矩形上方添加文字描述。具体参数解释可参考前面的文章:《Python 数据分析三剑客之 Matplotlib(二):文本描述 / 中文支持 / 画布 / 网格等基本图像属性》 1234567891011121314151617181920212223242526272829303132import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']y = np.arange(5)width1 = np.array([5, 7, 4, 3, 2])width2 = np.array([2, 4, 6, 7, 3])width3 = np.array([3, 1, 7, 5, 2])height = 0.3# 绘制不同数据时,y 轴依次增加一个偏移量plt.barh(y, width1, height, label='bar1')plt.barh(y + height, width2, height, label='bar2')plt.barh(y + height * 2, width3, height, label='bar3')# 依次添加每条数据的标签for a, b in zip(width1, y): plt.text(a, b-0.05, a)for c, d in zip(width2, y): plt.text(c, d+0.20, c)for e, f in zip(width3, y): plt.text(e, f+0.50, e)# 设置 y 轴刻度的标签plt.yticks(y + height, ['A', 'B', 'C', 'D', 'E'])plt.title('条形图添加文字描述示例')plt.xlabel('x axis label')plt.ylabel('y axis label')plt.legend()plt.show() 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105952856未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Matplotlib","slug":"Python-数据分析/Matplotlib","permalink":"https://www.itrhx.com/categories/Python-数据分析/Matplotlib/"}],"tags":[{"name":"Matplotlib","slug":"Matplotlib","permalink":"https://www.itrhx.com/tags/Matplotlib/"},{"name":"直方图","slug":"直方图","permalink":"https://www.itrhx.com/tags/直方图/"},{"name":"柱状图","slug":"柱状图","permalink":"https://www.itrhx.com/tags/柱状图/"},{"name":"条形图","slug":"条形图","permalink":"https://www.itrhx.com/tags/条形图/"}]},{"title":"Python 数据分析三剑客之 Matplotlib(五):散点图的绘制","slug":"A72-Matplotlib-05","date":"2020-04-18T15:50:09.015Z","updated":"2020-08-06T03:12:26.597Z","comments":true,"path":"2020/04/18/A72-Matplotlib-05/","link":"","permalink":"https://www.itrhx.com/2020/04/18/A72-Matplotlib-05/","excerpt":"","text":"Matplotlib 系列文章: Python 数据分析三剑客之 Matplotlib(一):初识 Matplotlib 与其 matplotibrc 配置文件 Python 数据分析三剑客之 Matplotlib(二):文本描述 / 中文支持 / 画布 / 网格等基本图像属性 Python 数据分析三剑客之 Matplotlib(三):图例 / LaTeX / 刻度 / 子图 / 补丁等基本图像属性 Python 数据分析三剑客之 Matplotlib(四):线性图的绘制 Python 数据分析三剑客之 Matplotlib(五):散点图的绘制 Python 数据分析三剑客之 Matplotlib(六):直方图 / 柱状图 / 条形图的绘制 Python 数据分析三剑客之 Matplotlib(七):饼状图的绘制 Python 数据分析三剑客之 Matplotlib(八):等高线 / 等值线图的绘制 Python 数据分析三剑客之 Matplotlib(九):极区图 / 极坐标图 / 雷达图的绘制 Python 数据分析三剑客之 Matplotlib(十):3D 图的绘制 Python 数据分析三剑客之 Matplotlib(十一):最热门最常用的 50 个图表【译文】 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105914929未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【1x00】方法描述matplotlib.pyplot.scatter() 方法可用于绘制散点图。 本文用到的其他图像属性可参考前面的两篇文章: 《Python 数据分析三剑客之 Matplotlib(二):文本描述 / 中文支持 / 画布 / 网格等基本图像属性》《Python 数据分析三剑客之 Matplotlib(三):图例 / LaTeX / 刻度 / 子图等基本图像属性》 基本语法:matplotlib.pyplot.scatter(x, y, s=None, c=None, marker=None, cmap=None, alpha=None, linewidths=None, edgecolors=None, \\*\\*kwargs) 参数 描述 x,y 数据位置,标量或类似数组的形式 s 标记的大小,以磅为单位,默认 rcParams['lines.markersize'] ** 2,即 6**2=36 color / c 标记的颜色,可以是单个颜色或者一个颜色列表支持英文颜色名称及其简写、十六进制颜色码等,更多颜色示例参见官网 Color Demo marker 标记的样式,默认为 rcParams["scatter.marker"] = 'o',更多样式参见表一 cmap 将浮点数映射成颜色的颜色映射表,即一个 Colormap 实例或注册的颜色表名,仅当 c 是浮点数数组时才使用 cmap alpha 标记的透明度,float 类型,取值范围:[0, 1],默认为 1.0,即不透明 linewidths 标记边缘的线宽,默认为 rcParams["lines.linewidth"] = 1.5 edgecolors 标记边缘的颜色,可以是单个颜色或者一个颜色列表支持英文颜色名称及其简写、十六进制颜色码等,更多颜色示例参见官网 Color Demo 表一:marker 标记的样式 标记 描述 "." 点 "," 像素点 "o" 圆圈 "v" 倒三角 "^" 正三角 "<" 左三角 ">" 右三角 "1" 倒三叉星 "2" 正三叉星(类似奔驰车标形状) "3" 左三叉星 "4" 右三叉星 "8" 八边形 "s" 正方形 "p" 五边形 "P" 填充的加号(粗加号) "+" 加号 "*" 星形 "h" 六边形(底部是角) "H" 六边形(底部是边) "x" x 号 "X" 填充的 x 号(粗 x 号) "D" 粗菱形(对角线相等) "d" 细菱形(对角线不等) `” “` 垂直线 "_" 水平线 0 水平线靠左 1 水平线靠右 2 垂直线靠上 3 垂直线靠下 4 左三角(比 "<" 更细) 5 右三角(比 ">" 更细) 6 正三角(比 "^" 更细) 7 倒三角(比 "v" 更细) 8 左三角(比 "<" 更细,靠左显示) 9 右三角(比 ">" 更细,靠右显示) 10 正三角(比 "^" 更细,靠上显示) 11 倒三角(比 "v" 更细,靠下显示) "None" / " " / "" 无样式 '$...$' 支持 LaTeX 数学公式,表达式用美元符号包围起来 【2x00】简单示例12345678import numpy as npimport matplotlib.pyplot as pltx = np.arange(0, 10, 1)y = np.array([3, 8, 1, 5, 7, 2, 3, 4, 5, 7])plt.scatter(x, y)plt.show() 【3x00】多条数据绘制多条数据,设置不同数据,然后多次调用 plt.scatter() 函数即可,不同数据的线条颜色会不同,系统随机,可单独指定不同颜色。 123456789101112131415161718import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = np.arange(-2*np.pi, 2*np.pi, 1)y1 = np.sin(3*x)/xy2 = np.sin(2*x)/xy3 = np.sin(1*x)/xplt.title('多数据散点图示例')plt.xlabel('x 轴')plt.ylabel('y 轴')plt.scatter(x, y1)plt.scatter(x, y2)plt.scatter(x, y3)plt.show() 【4x00】设置颜色 / 样式 / 图例1234567891011121314151617181920import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = np.arange(-2*np.pi, 2*np.pi, 1)y1 = np.sin(3*x)/xy2 = np.sin(2*x)/xy3 = np.sin(1*x)/xplt.title('散点图自定义样式示例')plt.xlabel('x 轴')plt.ylabel('y 轴')plt.scatter(x, y1, color='g', s=30, label='(x, y1)') # 默认绿色样式plt.scatter(x, y2, color='r', s=40, marker='d', label='(x, y2)') # 红色菱形plt.scatter(x, y3, color='b', s=50, marker='2', label='(x, y3)') # 蓝色正三叉星plt.legend(framealpha=0) # 显示图例,设置为全透明plt.show() 【5x00】指定位置显示文本注释matplotlib.pyplot.annotate() 方法可以在指定位置显示文本注释,参数解释常见前文:《Python 数据分析三剑客之 Matplotlib(二):文本描述 / 中文支持 / 画布 / 网格等基本图像属性》 应用举例: 1234567891011121314151617181920212223import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = [0.13, 0.22, 0.39, 0.59, 0.68, 0.74, 0.93]y = [0.75, 0.34, 0.44, 0.52, 0.80, 0.25, 0.55]plt.title('散点图添加文本注释示例')plt.xlabel('x 轴')plt.ylabel('y 轴')plt.xlim([0, 1]) # 设置 x 轴刻度的范围plt.ylim([0, 1]) # 设置 y 轴刻度的范围plt.scatter(x, y, marker='o', s=50)for m, n in zip(x, y): plt.annotate('(%s,%s)' % (m, n), xy=(m, n), xytext=(0, -10), textcoords='offset points', ha='center', # 点在注释文本的中心 va='top') # 点在注释文本的上方plt.show() 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105914929未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【6x00】随机数据散点图随机数据可以用 numpy 的 random 模块来实现。 numpy.random.rand(d0, d1, …, dn):根据给定维度生成 [0,1) 之间的数据。 numpy.random.randn(d0, d1, …, dn) :返回一个或一组具有标准正态分布的样本。 numpy.random.randint(low, high, size):返回随机整数,范围区间为 [low,high),size 为数组维度大小 应用举例: 123456789101112131415import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']N = 1000x = np.random.randn(N)y = np.random.randn(N)plt.title('散点图随机数据示例')plt.xlabel('x 轴')plt.ylabel('y 轴')plt.scatter(x, y, alpha=0.5)plt.show() 【7x00】随机颜色与色条12345678910111213141516171819import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']N = 1000x = np.random.randn(N)y = np.random.randn(N)color = np.random.rand(N)size = np.random.rand(N) * 1000plt.figure(figsize=(8.4, 5.8)) # 设置画布大小 840x580plt.title('散点图随机大小颜色示例')plt.xlabel('x 轴')plt.ylabel('y 轴')plt.scatter(x, y, c=color, s=size, alpha=0.5)plt.show() 可以用 pyplot.colorbar() 方法绘制颜色对照条。 基本语法:matplotlib.pyplot.colorbar([mappable=None, cax=None, ax=None, **kw]) 部分参数解释如下表,其他参数,如长度,宽度等请参考官方文档。 参数 描述 mappable 要设置色条的图像对象,该参数对于 Figure.colorbar 方法是必需的,但对于 pyplot.colorbar 函数是可选的 cax 可选项,要绘制色条的轴 ax 可选项,设置色条的显示位置,通常在一个画布上有多个子图时使用 **kw 可选项,其他关键字参数,参考官方文档 1234567891011121314151617181920import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']N = 1000x = np.random.randn(N)y = np.random.randn(N)color = np.random.rand(N)size = np.random.rand(N) * 1000plt.figure(figsize=(8.4, 5.8))plt.title('散点图颜色对照条示例')plt.xlabel('x 轴')plt.ylabel('y 轴')plt.scatter(x, y, c=color, s=size, alpha=0.5)plt.colorbar()plt.show() 【8x00】不同图像之间的层级调整zorder 参数用于设置不同图像之间的层级关系,数字越大,所处的层级越大,即显示越靠上。 未设置 zorder 参数前: 12345678910111213141516171819import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x1 = np.arange(-2*np.pi, 2*np.pi, 0.01)y1 = np.sin(3*x1)/x1x2 = np.arange(-2*np.pi, 2*np.pi, 1)y2 = np.sin(3*x2)/x2plt.title('不同图像之间层级调整示例')plt.xlabel('x 轴')plt.ylabel('y 轴')plt.plot(x1, y1, c='b', linewidth=3.5, label='线性图')plt.scatter(x2, y2, c='r', s=40, label='散点图')plt.legend()plt.show() 设置 zorder 参数后: 12345678910111213141516171819import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x1 = np.arange(-2*np.pi, 2*np.pi, 0.01)y1 = np.sin(3*x1)/x1x2 = np.arange(-2*np.pi, 2*np.pi, 1)y2 = np.sin(3*x2)/x2plt.title('不同图像之间层级调整示例')plt.xlabel('x 轴')plt.ylabel('y 轴')plt.plot(x1, y1, zorder=1, c='b', linewidth=3.5, label='线性图')plt.scatter(x2, y2, zorder=2, c='r', s=40, label='散点图')plt.legend()plt.show() 【9x00】框选部分数据有时候我们希望能够框选一部分数据来强调其重要性,matplotlib.patches.Polygon() 方法的作用是生成不规则的多边形补丁,matplotlib.patches 还有另外的方法可以生成矩形、圆形等其他图形,具体参见前面的文章《Python 数据分析三剑客之 Matplotlib(三):图例 / LaTeX / 刻度 / 子图 / 补丁等基本图像属性》,生成补丁之后,通过 axes.add_patch() 方法将其添加到绘图区(axes)即可。 1234567891011121314151617181920212223242526272829303132import numpy as npimport matplotlib.pyplot as pltimport matplotlib.patches as mpathesplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']plt.figure(figsize=(8.4, 5.8))x1 = np.arange(0, 1000, 10)y1 = np.random.randint(0, 1000, 100)x2 = np.arange(0, 500, 10)y2 = np.random.randint(200, 800, 50)x3 = np.random.randint(50, 800, 80)y3 = np.random.randint(50, 800, 80)x4 = np.array([0, 100, 300, 400, 350, 500, 450, 367, 420, 490])y4 = np.array([267, 800, 453, 500, 600, 420, 380, 503, 390, 600])plt.title('散点图数据框选示例', fontsize=15)plt.xlabel('x 轴', fontsize=15)plt.ylabel('y 轴', fontsize=15)plt.scatter(x1, y1, c='r', s=50, alpha=0.7, label='RED')plt.scatter(x2, y2, c='b', s=100, alpha=0.7, label='BLUE')plt.scatter(x3, y3, c='g', s=150, alpha=0.7, label='GREEN')plt.scatter(x4, y4, c='y', s=250, alpha=0.7, label='YELLOW')plt.legend(loc='upper right', borderpad=1, edgecolor='k', framealpha=1, labelspacing=1)Polygon_point = [[100, 800], [0, 267], [500, 420], [490, 600]] # 多边形补丁的顶点坐标polygon = mpathes.Polygon(Polygon_point, color='#FF69B4', alpha=0.3) # 绘制补丁,框选部分数据ax = plt.gca() # 获取当前绘图区(gca = Get Current Axes)ax.add_patch(polygon) # 将补丁添加到当前绘图区plt.show() 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105914929未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Matplotlib","slug":"Python-数据分析/Matplotlib","permalink":"https://www.itrhx.com/categories/Python-数据分析/Matplotlib/"}],"tags":[{"name":"Matplotlib","slug":"Matplotlib","permalink":"https://www.itrhx.com/tags/Matplotlib/"},{"name":"散点图","slug":"散点图","permalink":"https://www.itrhx.com/tags/散点图/"}]},{"title":"Python 数据分析三剑客之 Matplotlib(四):线性图的绘制","slug":"A71-Matplotlib-04","date":"2020-04-16T14:42:30.239Z","updated":"2020-08-06T03:10:59.357Z","comments":true,"path":"2020/04/16/A71-Matplotlib-04/","link":"","permalink":"https://www.itrhx.com/2020/04/16/A71-Matplotlib-04/","excerpt":"","text":"Matplotlib 系列文章: Python 数据分析三剑客之 Matplotlib(一):初识 Matplotlib 与其 matplotibrc 配置文件 Python 数据分析三剑客之 Matplotlib(二):文本描述 / 中文支持 / 画布 / 网格等基本图像属性 Python 数据分析三剑客之 Matplotlib(三):图例 / LaTeX / 刻度 / 子图 / 补丁等基本图像属性 Python 数据分析三剑客之 Matplotlib(四):线性图的绘制 Python 数据分析三剑客之 Matplotlib(五):散点图的绘制 Python 数据分析三剑客之 Matplotlib(六):直方图 / 柱状图 / 条形图的绘制 Python 数据分析三剑客之 Matplotlib(七):饼状图的绘制 Python 数据分析三剑客之 Matplotlib(八):等高线 / 等值线图的绘制 Python 数据分析三剑客之 Matplotlib(九):极区图 / 极坐标图 / 雷达图的绘制 Python 数据分析三剑客之 Matplotlib(十):3D 图的绘制 Python 数据分析三剑客之 Matplotlib(十一):最热门最常用的 50 个图表【译文】 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105839855未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【1x00】方法描述matplotlib.pyplot.plot() 函数可以用于绘制线性图。 本文用到的其他图像属性可参考前面的两篇文章: 《Python 数据分析三剑客之 Matplotlib(二):文本描述 / 中文支持 / 画布 / 网格等基本图像属性》《Python 数据分析三剑客之 Matplotlib(三):图例 / LaTeX / 刻度 / 子图等基本图像属性》 基本语法:matplotlib.pyplot.plot(x, y[, fmt, \\*\\*kwargs]) 参数 描述 x x 轴数据,数组类型或者标量,x 值是可选的,默认为 range(len(y)),通常为一维数组 y y 轴数据,数组类型或者标量,通常为一维数组 fmt str 类型,格式字符串,由标记、线条和颜色部分组成fmt = '[marker][line][color]',例如 ro 表示红色圆圈,三个参数的取值见后表 **kwargs 可选项,其他 Line2D 属性,常用属性见下表 部分常见 Line2D 属性如下表,完整属性参见官方文档。 属性 描述 alpha 线条透明度,float 类型,取值范围:[0, 1],默认为 1.0,即不透明 antialiased / aa 是否使用抗锯齿渲染,默认为 True color / c 线条颜色,支持英文颜色名称及其简写、十六进制颜色码等,更多颜色示例参见官网 Color Demo linestyle / ls 线条样式:'-' or 'solid', '--' or 'dashed', '-.' or 'dashdot' ':' or 'dotted', 'none' or ' ' or '' linewidth / lw 线条宽度,float 类型,默认 0.8 markeredgecolor / mec marker 标记的边缘颜色 markeredgewidth / mew marker 标记的边缘宽度 markerfacecolor / mfc marker 标记的颜色 markerfacecoloralt / mfcalt marker 标记的备用颜色 markersize / ms marker 标记的大小 fmt 中 marker、line、color 三个参数的取值: marker:线条标记样式(线条上每个数据点的样式) 字符 描述 '.' 点标记(point marker) ',' 像素点标记(pixel marker) 'o' 圆圈标记(circle marker) 'v' 下三角标记(triangle_down marker) '^' 上三角标记(triangle_up marker) '<' 左三角标记(triangle_left marker) '>' 右三角标记(triangle_right marker) '1' 下三叉星标记(tri_down marker) '2' 上三叉星标记(tri_up marker) '3' 左三叉星标记(tri_left marker) '4' 右三叉星标记(tri_right marker) 's' 正方形标记(square marker) 'p' 五角形标记(pentagon marker) '*' 星号标记(star marker) 'h' 六边形标记(hexagon1 marker) 'H' 六边形标记(hexagon2 marker) '+' 加号标记(plus marker) 'x' X 号标记(x marker) 'D' 菱形标记(diamond marker) 'd' 细菱形标记(thin_diamond marker) `’ ‘` 垂直线标记(vline marker) '_' 水平线标记(hline marker) line:线条样式 字符 描述 '-' 实线样式(solid line style) '--' 虚线样式(dashed line style) '-.' 点划线样式(dash-dot line style) ':' 点样式(dotted line style) color:线条颜色,支持英文颜色名称及其简写、十六进制颜色码等 字符 描述 'b' 蓝色(blue) 'g' 绿色(green) 'r' 红色(red) 'c' 青色(cyan) 'm' 品红(magenta) 'y' 黄色(yellow) 'k' 黑色(black) 'w' 白色(white) fmt 举例: 12345'b' # 默认形状的蓝色标记'or' # 红圈'-g' # 绿色实线'--' # 默认颜色的虚线'^k:' # 黑色三角形标记,由虚线连接 【2x00】基本示例12345678910111213import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei'] # 设置显示中文x = np.arange(-2*np.pi, 2*np.pi, 0.01)y = np.sin(3*x)/xplt.title('线性图示例') # 设置标题plt.xlabel('x 轴') # 设置 x 轴标签plt.ylabel('y 轴') # 设置 y 轴标签plt.plot(x, y)plt.show() 【3x00】多条数据绘制多条数据,设置不同数据,然后多次调用 plt.plot() 函数即可,不同数据的线条颜色会不同,系统随机,可单独指定不同颜色。 123456789101112131415161718import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = np.arange(-2*np.pi, 2*np.pi, 0.01)y1 = np.sin(3*x)/xy2 = np.sin(2*x)/xy3 = np.sin(1*x)/xplt.title('多数据线性图示例')plt.xlabel('x 轴')plt.ylabel('y 轴')plt.plot(x, y1)plt.plot(x, y2)plt.plot(x, y3)plt.show() 【4x00】设置颜色 / 样式 / 图例设置线条颜色样式等属性直接在 plot() 函数里面添加相应参数即可,设置图例则需要调用 legend() 方法。 12345678910111213141516171819202122import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x1 = np.arange(-2*np.pi, 2*np.pi, 0.01)y1 = np.sin(3*x1)/x1y2 = np.sin(2*x1)/x1x3 = np.arange(-2*np.pi, 2*np.pi, 2)y3 = np.array([0, 2, 1.5, 1, 2.4, -0.2, 1.7])plt.title('线性图自定义样式示例')plt.xlabel('x 轴')plt.ylabel('y 轴')plt.plot(x1, y1, '--r', label='x1, y1') # 线条样式为 --,颜色为 r(红色)plt.plot(x1, y2, color='green', label='x1, y2') # 样式默认,颜色为绿色plt.plot(x3, y3, marker='o', mfc='r', linestyle=':', label='x3, y3') # 标记样式为 o,颜色为 r(红色),线条样式为 :plt.legend(edgecolor='#87A3CC', facecolor='#F5F5F5') # 图例plt.show() 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105839855未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【5x00】设置刻度调用 xticks() 和 yticks() 函数可以对坐标刻度进行自定义,该函数接收两个参数,第一个参数表示要显示的刻度位置,第二个参数表示在对应刻度线上要显示的标签信息,标签信息支持 LeTeX 数学公式,使用时要用美元符号 $ 包围起来。 12345678910111213141516171819202122import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = np.arange(-2*np.pi, 2*np.pi, 0.01)y1 = np.sin(3*x)/xy2 = np.sin(2*x)/xy3 = np.sin(1*x)/xplt.title('线性图设置刻度示例')plt.xlabel('x 轴')plt.ylabel('y 轴')plt.plot(x, y1, '--r', label='sin(3*x)/x')plt.plot(x, y2, color='green', linestyle=':', label='sin(2*x)/x')plt.plot(x, y3, label='sin(1*x)/x')plt.legend(edgecolor='#87A3CC', facecolor='#F5F5F5')plt.xticks((-2*np.pi, -np.pi, 0, np.pi, 2*np.pi), (r'$-2\\pi$', r'$-\\pi$', '$0$', r'$\\pi$', r'$2\\pi$'))plt.yticks((-1, 0, 1, 2, 3))plt.show() 【6x00】隐藏画布边框Matplotlib 所绘制的图表中的每个绘图元素,例如线条 Line2D、文字 Text、刻度等在内存中都有一个对象与之对应。 matplotlib.pyplot.gca() 函数用于获取当前的绘图区 Axes(Get Current Axes) matplotlib.pyplot.gcf() 函数用于获取当前的画布 Figure(Get Current Figure) 例如:matplotlib.pyplot.plot() 实际上会通过 matplotlib.pyplot.gca() 获得当前的 Axes 对象 ax,然后再调用 ax.plot() 方法实现真正的绘图。我们可以通过这种方法来实现画布边框的隐藏和坐标轴的移动。 12345678910111213141516171819202122232425262728import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = np.arange(-2*np.pi, 2*np.pi, 0.01)y1 = np.sin(3*x)/xy2 = np.sin(2*x)/xy3 = np.sin(1*x)/xplt.title('线性图隐藏画布边框示例')plt.xlabel('x 轴')plt.ylabel('y 轴')plt.plot(x, y1, '--r', label='sin(3*x)/x')plt.plot(x, y2, color='green', linestyle=':', label='sin(2*x)/x')plt.plot(x, y3, label='sin(1*x)/x')plt.legend(edgecolor='#87A3CC', facecolor='#F5F5F5')plt.xticks((-2*np.pi, -np.pi, 0, np.pi, 2*np.pi), (r'$-2\\pi$', r'$-\\pi$', '$0$', r'$\\pi$', r'$2\\pi$'))plt.yticks((-1, 0, 1, 2, 3))ax = plt.gca() # 获取当前的画布, gca = get current axesax.spines['right'].set_visible(False) # 获取绘图区的轴对象(spines),设置右边框不显示ax.spines['top'].set_visible(False) # 获取绘图区的轴对象(spines),设置上边框不显示# ax.spines['right'].set_color('none') # 设置颜色为 none,效果与上面的一致# ax.spines['top'].set_color('none')plt.show() 【7x00】移动坐标轴123456789101112131415161718192021222324252627282930313233import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = np.arange(-2*np.pi, 2*np.pi, 0.01)y1 = np.sin(3*x)/xy2 = np.sin(2*x)/xy3 = np.sin(1*x)/xplt.title('线性图移动坐标轴示例')plt.xlabel('x 轴')plt.ylabel('y 轴')plt.plot(x, y1, '--r', label='sin(3*x)/x')plt.plot(x, y2, color='green', linestyle=':', label='sin(2*x)/x')plt.plot(x, y3, label='sin(1*x)/x')plt.legend(edgecolor='#87A3CC', facecolor='#F5F5F5')plt.xticks((-2*np.pi, -np.pi, 0, np.pi, 2*np.pi), (r'$-2\\pi$', r'$-\\pi$', '$0$', r'$\\pi$', r'$2\\pi$'))plt.yticks((-1, 0, 1, 2, 3))ax = plt.gca() # 获取当前的画布, gca = get current axesax.spines['right'].set_visible(False) # 获取绘图区的轴对象(spines),设置右边框不显示ax.spines['top'].set_visible(False) # 获取绘图区的轴对象(spines),设置上边框不显示# ax.spines['right'].set_color('none') # 设置颜色为 none,效果与上面的一致# ax.spines['top'].set_color('none')ax.spines['left'].set_position(('data', 0)) # 设置两个坐标轴在(0, 0)位置相交ax.spines['bottom'].set_position(('data', 0))ax.xaxis.set_ticks_position('bottom') # 设置 x 坐标轴标签的位置ax.yaxis.set_ticks_position('left') # 设置 y 坐标轴标签的位置plt.show() 【8x00】指定位置显示文本matplotlib.pyplot.annotate() 方法可以在指定坐标点添加文本或 LaTeX 描述,也可以在其他位置添加描述后,使用箭头指向某个坐标点。 基本语法:matplotlib.pyplot.annotate(text, xy, xytext, xycoords, textcoords, ha, va, arrowprops, \\*\\*kwargs) 参数 描述 text str 类型,注释的文本 xy 被注释的坐标点,格式:(x, y) xytext 注释文本的坐标点,格式:(x, y),默认与 xy 相同 xycoords 被注释的坐标点的参考系,取值参见表一,默认为 ‘data’ textcoords 注释文本的坐标点的参考系,取值参见表二,默认为 xycoords 的值 ha 注释点在注释文本的左边、右边或中间(left、right、center) va 注释点在注释文本的上边、下边、中间或基线 (top、bottom、center、baseline) arrowprops dict 字典类型,箭头的样式如果 arrowprops 不包含键 arrowstyle,则允许的键参见表三如果 arrowprops 包含键 arrowstyle,则允许的键参见表四 表一:xycoords 取值类型 取值 描述 ‘figure points’ 以画布左下角为参考,单位为点数 ‘figure pixels’ 以画布左下角为参考,单位为像素 ‘figure fraction’ 以画布左下角为参考,单位为百分比 ‘axes points’ 以绘图区左下角为参考,单位为点数 ‘axes pixels’ 以绘图区左下角为参考,单位为像素 ‘axes fraction’ 以绘图区左下角为参考,单位为百分比 ‘data’ 使用被注释对象的坐标系,即数据的 x, y 轴(默认) ‘polar’ 使用(θ,r)形式的极坐标系 表二:textcoords 取值类型 取值 描述 ‘figure points’ 以画布左下角为参考,单位为点数 ‘figure pixels’ 以画布左下角为参考,单位为像素 ‘figure fraction’ 以画布左下角为参考,单位为百分比 ‘axes points’ 以绘图区左下角为参考,单位为点数 ‘axes pixels’ 以绘图区左下角为参考,单位为像素 ‘axes fraction’ 以绘图区左下角为参考,单位为百分比 ‘data’ 使用被注释对象的坐标系,即数据的 x, y 轴 ‘polar’ 使用(θ,r)形式的极坐标系 ‘offset points’ 相对于被注释点的坐标 xy 的偏移量,单位是点 ‘offset pixels’ 相对于被注释点的坐标 xy 的偏移量,单位是像素 表三:arrowprops 不包含键 arrowstyle 时的取值 键 描述 width 箭头的宽度,以点为单位 headwidth 箭头底部的宽度,以点为单位 headlength 箭头的长度,以点为单位 shrink 箭头两端收缩占总长的百分比 ? 其他 matplotlib.patches.FancyArrowPatch 中的关键字,部分常用关键字参见表五 表四:arrowprops 包含键 arrowstyle 时的取值 取值 描述 '-' None '->' head_length=0.4,head_width=0.2 '-[' widthB=1.0,lengthB=0.2,angleB=None ']-' widthA=1.0, lengthA=0.2, angleA=None ]-[ widthA=1.0, lengthA=0.2, angleA=None, widthB=1.0, lengthB=0.2, angleB=None `’ - ‘` widthA=1.0,widthB=1.0 `’- >’` head_length=0.4,head_width=0.2 '<-' head_length=0.4,head_width=0.2 '<->' head_length=0.4,head_width=0.2 `’< -‘` head_length=0.4,head_width=0.2 `’< - >’` head_length=0.4,head_width=0.2 'fancy' head_length=0.4,head_width=0.4,tail_width=0.4 'simple' head_length=0.5,head_width=0.5,tail_width=0.2 'wedge' tail_width=0.3,shrink_factor=0.5 表五:matplotlib.patches.FancyArrowPatch 常用的键 键 描述 arrowstyle 箭头样式,取值参见表四 connectionstyle 连接方式,默认为 arc3,有以下五种取值:angle:angleA=90, angleB=0, rad=0.0angle3:angleA=90, angleB=0arc:angleA=0, angleB=0, armA=None, armB=None, rad=0.0arc3:rad=0.0bar:armA=0.0, armB=0.0, fraction=0.3, angle=Noneangle 为箭头旋转的角度,rad 为弧度 relpos 箭头起始点相对注释文本的位置,默认为 (0.5, 0.5),即文本的中心(0,0)表示左下角,(1,1)表示右上角 patchA 箭头起点处的图形,默认为文本的边框 patchB 箭头终点处的图形,默认为空 shrinkA 箭头起点的缩进点数,默认为2 shrinkB 箭头终点的缩进点数,默认为2 ? 其他键值,参见官方文档 matplotlib.patches.PathPatch connectionstyle 样式举例 应用举例: 1234567891011121314151617181920212223242526272829303132333435363738394041import numpy as npimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = np.arange(-2*np.pi, 2*np.pi, 0.01)y1 = np.sin(3*x)/xy2 = np.sin(2*x)/xy3 = np.sin(1*x)/xplt.title('线性图显示文本注释示例')plt.xlabel('x 轴')plt.ylabel('y 轴')plt.plot(x, y1, '--r', label='sin(3*x)/x')plt.plot(x, y2, color='green', linestyle=':', label='sin(2*x)/x')plt.plot(x, y3, label='sin(1*x)/x')plt.legend(edgecolor='#87A3CC', facecolor='#F5F5F5')plt.xticks((-2*np.pi, -np.pi, 0, np.pi, 2*np.pi), (r'$-2\\pi$', r'$-\\pi$', '$0$', r'$\\pi$', r'$2\\pi$'))plt.yticks((-1, 0, 1, 2, 3))ax = plt.gca() # 获取当前的画布, gca = get current axesax.spines['right'].set_visible(False) # 获取绘图区的轴对象(spines),设置右边框不显示ax.spines['top'].set_visible(False) # 获取绘图区的轴对象(spines),设置上边框不显示# ax.spines['right'].set_color('none') # 设置颜色为 none,效果与上面的一致# ax.spines['top'].set_color('none')ax.spines['left'].set_position(('data', 0)) # 设置两个坐标轴在(0, 0)位置相交ax.spines['bottom'].set_position(('data', 0))ax.xaxis.set_ticks_position('bottom') # 设置 x 坐标轴标签的位置ax.yaxis.set_ticks_position('left') # 设置 y 坐标轴标签的位置plt.annotate(r'$\\lim_{x\\to 0}\\frac{\\sin(x)}{x}=1$', # 插入 LaTeX 表达式 xy=[0, 1], # 被标记的坐标 xycoords='data', # 被标记的坐标的参考系 xytext=[30, 30], # 注释文本的坐标 textcoords='offset points', # 注释文本的坐标的参考系 fontsize=16, # 字体大小 arrowprops=dict(arrowstyle=\"->\", connectionstyle=\"arc3, rad=.2\")) # 箭头样式plt.show() 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105839855未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Matplotlib","slug":"Python-数据分析/Matplotlib","permalink":"https://www.itrhx.com/categories/Python-数据分析/Matplotlib/"}],"tags":[{"name":"Matplotlib","slug":"Matplotlib","permalink":"https://www.itrhx.com/tags/Matplotlib/"},{"name":"线性图","slug":"线性图","permalink":"https://www.itrhx.com/tags/线性图/"}]},{"title":"Python 数据分析三剑客之 Matplotlib(三):图例/LaTeX/刻度/子图/补丁等基本图像属性","slug":"A70-Matplotlib-03","date":"2020-04-14T13:36:35.265Z","updated":"2020-08-06T03:09:34.783Z","comments":true,"path":"2020/04/14/A70-Matplotlib-03/","link":"","permalink":"https://www.itrhx.com/2020/04/14/A70-Matplotlib-03/","excerpt":"","text":"Matplotlib 系列文章: Python 数据分析三剑客之 Matplotlib(一):初识 Matplotlib 与其 matplotibrc 配置文件 Python 数据分析三剑客之 Matplotlib(二):文本描述 / 中文支持 / 画布 / 网格等基本图像属性 Python 数据分析三剑客之 Matplotlib(三):图例 / LaTeX / 刻度 / 子图 / 补丁等基本图像属性 Python 数据分析三剑客之 Matplotlib(四):线性图的绘制 Python 数据分析三剑客之 Matplotlib(五):散点图的绘制 Python 数据分析三剑客之 Matplotlib(六):直方图 / 柱状图 / 条形图的绘制 Python 数据分析三剑客之 Matplotlib(七):饼状图的绘制 Python 数据分析三剑客之 Matplotlib(八):等高线 / 等值线图的绘制 Python 数据分析三剑客之 Matplotlib(九):极区图 / 极坐标图 / 雷达图的绘制 Python 数据分析三剑客之 Matplotlib(十):3D 图的绘制 Python 数据分析三剑客之 Matplotlib(十一):最热门最常用的 50 个图表【译文】 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105828143未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【1x00】设置图例matplotlib.pyplot.legend() 方法可以为图表设置图例。 基本语法:matplotlib.pyplot.legend(\\*args, \\*\\*kwargs) 部分常见参数: 参数 描述 loc 图例在画布中的位置,默认为 best,其他取值:best, upper right, upper left, lower left lower right, right, center left, center right lower center, upper center, center也可以用数字 0 - 10 来表示上述位置 bbox_to_anchor 调整图例在画布中的位置,当 loc 达不到我们想要的效果时,就可以使用该参数该参数接收一个二元数组 (x, y),x 用于控制图例的左右移动,值越大越向右边移动y 用于控制图例的上下移动,值越大,越向上移动 borderaxespad 图例距离轴之间的距离,float 类型,默认为 0.5 borderpad 图例边框空白区域大小,float 类型,默认为 0.4 columnspacing 图例列间距,float 类型,默认为 2.0 edgecolor 图例边缘线颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo facecolor 图例背景颜色,默认继承自 axes.facecolor其他颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo fancybox 是否使用圆形框作为图例背景, 默认为 True fontsize 图例字体大小,默认为 medium,xx-small, x-small, small, medium large, x-large, xx-large, smaller, larger也可以使用数字来表示字体大小 framealpha 图例透明度,float 类型,默认为 0.8,取值范围:[0, 1] handleheight 图例的高度 ,float 类型,默认为 0.7 handlelength 图例的宽度,float 类型,默认为 2.0 handletextpad 图例和图例文本之间的水平距离,float 类型,默认为 0.8 labelspacing 不同图例之间的垂直距离,float 类型,默认为 0.5 shadow 是否给图例添加阴影效果,默认为 False 【1x01】方法一:指定 label 参数在画图的时候先指定 label 标签文本,再调用 legend() 方法即可。 12345678910111213import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = range(2, 26, 2)y = range(0, 12)a = [5, 10, 15, 20, 25, 30]b = [3, 4, 5, 6, 7, 8]plt.plot(a, b, label='图例一') # 指定 a,b 数据的图例plt.plot(x, y, label='图例二') # 指定 x,y 数据的图例plt.legend(loc=2, edgecolor='red', facecolor='#F5F5F5') # 指定图例位置、边缘线条颜色和背景色plt.show() 【1x02】方法二:使用 set_label 方法在画图的时候先使用 set_label() 方法指定标签文本,再调用 legend() 方法即可。 123456789101112131415import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = range(2, 26, 2)y = range(0, 12)a = [5, 10, 15, 20, 25, 30]b = [3, 4, 5, 6, 7, 8]line1, = plt.plot(a, b)line2, = plt.plot(x, y)line1.set_label('图例一') # 指定 a,b 数据的图例line2.set_label('图例二') # 指定 x,y 数据的图例plt.legend(loc=2, edgecolor='red', facecolor='#F5F5F5') # 指定图例位置、边缘线条颜色和背景色plt.show() 【1x03】方法三:直接使用 legend 方法直接使用 legend() 方法来指定图例标签也可以达到同样效果,图例以列表或者元组形式储存,图例与绘制图形的顺序一一对应。 12345678910111213import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = range(2, 26, 2)y = range(0, 12)a = [5, 10, 15, 20, 25, 30]b = [3, 4, 5, 6, 7, 8]plt.plot(a, b)plt.plot(x, y)plt.legend(['图例一', '图例二'], loc=2, edgecolor='red', facecolor='#F5F5F5')plt.show() 也可以使用两个元组,将绘制的图形和图例一一对应来储存: 12345678910111213import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = range(2, 26, 2)y = range(0, 12)a = [5, 10, 15, 20, 25, 30]b = [3, 4, 5, 6, 7, 8]line1, = plt.plot(a, b)line2, = plt.plot(x, y)plt.legend((line1, line2), ('图例一', '图例二'), loc=2, edgecolor='red', facecolor='#F5F5F5')plt.show() 以上三种方法绘制的图形均一致: 【2x00】数学公式 LaTeXLaTeX(LATEX,音译“拉泰赫”)是一种基于 TeX 的排版系统,常用于生成复杂表格和数学公式,Matplotlib 提供了自己的 TeX 表达式解析器,布局引擎和字体,布局引擎基于 Donald Knuth 的 TeX 布局算法改编。使用数学公式时用 $ 将其包围起来即可。具体的符号与其对应的英文表示参见官方文档:https://matplotlib.org/tutorials/text/mathtext.html 应用举例: 1234567891011121314import numpy as npimport matplotlib.pyplot as pltt = np.arange(0.0, 2.0, 0.01)s = np.sin(2*np.pi*t)plt.title(r'$\\alpha_i > \\beta_i$', fontsize=20)plt.text(1, -0.6, r'$\\sum_{i=0}^\\infty x_i$', fontsize=20)plt.text(0.6, 0.6, r'$\\mathcal{A}\\mathrm{sin}(2 \\omega t)$', fontsize=20)plt.xlabel('time (s)')plt.ylabel('volts (mV)')plt.plot(t, s)plt.show() 【3x00】调整 x / y 轴刻度和范围在生成图像时,默认会按照所给的数据均匀设置几个刻度,如果对默认的刻度不满意,则可以使用 xticks() 或 yticks() 方法指定刻度值。xlim() 与 ylim() 则可以设置刻度的范围。 基本语法:matplotlib.pyplot.xticks([ticks=None, labels=None, \\*\\*kwargs])matplotlib.pyplot.yticks([ticks=None, labels=None, \\*\\*kwargs]) 参数 描述 ticks 数组形式的位置列表,即显示第 n 个位置的刻度,可选项,若传递空列表将删除所有 xtick / ytick labels 数组形式的值,在对应刻度线显示的标签信息。仅当同时传递了刻度时,才能传递此参数 **kwargs 其他参数参见 Text 其他参数里面有一个常用的 rotation 参数,次参数可以用于设置刻度标签的旋转角度,对于标签太长的可以将其旋转一个角度来显示。 应用举例: 1234567891011import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = range(2, 26, 2)y = range(0, 12)plt.plot(x, y)# x 轴每隔三个显示一次刻度,旋转45°显示标签plt.xticks(range(2, 26, 3), ('the {} ticks'.format(i) for i in range(2, 26, 3)), rotation=45)plt.show() 12345678910import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = range(2, 26, 2)y = range(0, 12)plt.xlim((0, 30)) # 设置 x 轴刻度范围plt.plot(x, y)plt.show() 【4x00】画布边框与坐标轴的移动Matplotlib 所绘制的图表中的每个绘图元素,例如线条 Line2D、文字 Text、刻度等在内存中都有一个对象与之对应。 matplotlib.pyplot.gca() 函数用于获取当前的绘图区 Axes(Get Current Axes) matplotlib.pyplot.gcf() 函数用于获取当前的画布 Figure(Get Current Figure) 例如:matplotlib.pyplot.plot() 实际上会通过 matplotlib.pyplot.gca() 获得当前的 Axes对象 ax,然后再调用 ax.plot() 方法实现真正的绘图。我们可以通过这种方法来实现画布边框的隐藏和坐标轴的移动。 应用举例: 12345678910111213141516171819202122232425import matplotlib.pyplot as pltimport numpy as npx = np.arange(0, 2*np.pi, np.pi/100)y = np.sin(x)plt.plot(x, y)plt.xlabel('X axis')plt.ylabel('Y axis')ticks = (0, np.pi/2, np.pi, 3*np.pi/2, 2*np.pi)labels = ('0', r'$\\frac{\\pi} {2}$', r'$\\pi$', r'$\\frac{3\\pi} {2}$', r'$2\\pi$')plt.xticks(ticks, labels) # 设置 x 坐标轴显示的数据ax = plt.gca() # 获取当前的画布, gca = get current axesax.spines['right'].set_visible(False) # 设置右边框不显示ax.spines['top'].set_visible(False) # 设置上边框不显示# ax.spines['top'].set_color('none') # 设置颜色为无也可以ax.xaxis.set_ticks_position('bottom') # 设置 x 坐标轴的标签位置ax.yaxis.set_ticks_position('left') # 设置 y 坐标轴的标签位置ax.spines['bottom'].set_position(('data', 0)) # 设置 x 轴在 (0, 0) 位置ax.spines['left'].set_position(('data', 0)) # 设置 y 轴在 (0, 0) 位置plt.show() 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105828143未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【5x00】创建子图子图的概念:在同一张画布中创建多个图像,方便对数据进行对比。 【5x01】方法一:add_subplot()首先创建一个画布,然后利用 add_subplot() 方法填充子图,该方法接收三个参数,前两个参数表示子图有几行几列,最后一个参数表示第几个子图,如:fig.add_subplot(221) 表示总共有两行两列(2x2=4)一共4个子图,当前是第一个子图。若子图大于9个则用逗号隔开即可。 应用举例: 1234567891011121314151617181920import numpy as npimport matplotlib.pyplot as pltx = np.arange(100)fig = plt.figure(figsize=(12, 6))ax1 = fig.add_subplot(221) # 第 1 个子图ax1.plot(x, x)ax2 = fig.add_subplot(222) # 第 2 个子图ax2.plot(x, -x)ax3 = fig.add_subplot(223) # 第 3 个子图ax3.plot(x, x ** 2)ax4 = fig.add_subplot(224) # 第 4 个子图ax4.plot(-x, x ** 2)plt.show() 【5x02】方法二:pyplot.subplot()matplotlib.pyplot.subplot() 方法和 add_subplot() 方法有点儿类似,同样接收三个参数,前两个参数表示子图有几行几列,最后一个参数表示第几个子图。 1234567891011121314151617181920import numpy as npimport matplotlib.pyplot as pltx = np.arange(100)plt.figure(figsize=(12, 6))plt.subplot(221) # 第 1 个子图plt.plot(x, x)plt.subplot(222) # 第 2 个子图plt.plot(x, x ** 2)plt.subplot(223) # 第 3 个子图plt.plot(x, x ** 3)plt.subplot(224) # 第 4 个子图plt.plot(x, x ** 4)plt.show() 【5x03】方法三:pyplot.subplots()matplotlib.pyplot.subplots() 函数会将画布分割成指定的列和行,分割后依次在各个区域画图即可。注意与 matplotlib.pyplot.subplot() 略有差别。 fig, axes = plt.subplots 的意思是:plt.subplots 方法会返回一个包含 figure(画布) 和 axes(绘图区) 对象的元组,fig 和 axes 参数分别接收这两个对象,后期对不同绘图区进行处理即可。 123456789101112import numpy as npimport matplotlib.pyplot as pltx = np.arange(100)fig, axes = plt.subplots(figsize=(12, 6), nrows=2, ncols=2) # 将画布分割为2行2列,起始值为0axes[0][0].plot(x, x) # 绘制第1行第1列axes[0][1].plot(x, -x) # 绘制第1行第2列axes[1][0].plot(-x, x ** 2) # 绘制第2行第1列axes[1][1].plot(x, -x ** 2) # 绘制第2行第2列plt.show() 【6x00】填充补丁matplotlib.patches 可用于在画布上填充圆形、长方形、椭圆形、多边形等多种图像补丁。 官方文档:https://matplotlib.org/api/patches_api.html 类 描述 matplotlib.patches.Arc(xy, width, height, angle=0.0, theta1=0.0, theta2=360.0, **kwargs) 椭圆弧 matplotlib.patches.Arrow(x, y, dx, dy, width=1.0, **kwargs) 箭头 matplotlib.patches.Circle(xy, radius=5, **kwargs) 圆 matplotlib.patches.Ellipse(xy, width, height, angle=0, **kwargs) 椭圆 matplotlib.patches.CirclePolygon(xy, radius=5, resolution=20, **kwargs) 近似多边形的圆形面片 matplotlib.patches.Polygon(xy, closed=True, **kwargs) 不规则多边形 matplotlib.patches.Rectangle(xy, width, height, angle=0.0, **kwargs) 矩形 matplotlib.patches.RegularPolygon(xy, numVertices, radius=5, orientation=0, **kwargs) 正多边形 matplotlib.patches.Shadow(patch, ox, oy, props=None, **kwargs) 创建给定补丁的阴影 matplotlib.patches.Wedge(center, r, theta1, theta2, width=None, **kwargs) 楔形 应用举例: 1234567891011121314151617181920212223242526272829303132333435363738import numpy as npimport matplotlib.pyplot as pltimport matplotlib.patches as mpathesx = np.arange(0.0, 2.0, 0.01)y = np.sin(2*np.pi*x)# 获取当前绘图区(gca = Get Current Axesax = plt.gca()# 圆形:圆点(0.2, -0.25),半径0.2,红色circle = mpathes.Circle((0.2, -0.25), 0.2, color='r')# 长方形:左侧和底部坐标(0.25, 0.75),宽0.25,高0.15,透明度0.5rect = mpathes.Rectangle((0.25, 0.75), 0.25, 0.15, alpha=0.5)# 正多边形:中心点坐标(1.0, 0),顶点数6,中心到每个顶点的距离0.25regular_polygon = mpathes.RegularPolygon((1.0, 0), 6, 0.25, color='g')# 不规则多边形:polygon_point 为要连接的点的坐标polygon_point = [[1.5, -0.75], [1.75, -1], [2.0, 0], [1.5, -0.25]]polygon = mpathes.Polygon(polygon_point, color='#FF69B4', alpha=0.3)# 椭圆形:中心点坐标(1.25, 0.75),横轴长度0.4,垂直轴长度0.2ellipse = mpathes.Ellipse((1.25, 0.75), 0.4, 0.2, color='y')# 将补丁添加到当前绘图区ax.add_patch(circle)ax.add_patch(rect)ax.add_patch(regular_polygon)ax.add_patch(polygon)ax.add_patch(ellipse)plt.xlabel('x axis label')plt.ylabel('y axis label')plt.grid()plt.plot(x, y)plt.show() 【7x00】保存图像matplotlib.pyplot.savefig() 方法可以将绘制的图像保存到本地,支持多种格式:eps, pdf, pgf, png, ps, raw, rgba, svg, svgz。 注意:因为调用 plt.show() 函数后,会创建一个新的空白的图片,所以在保存图片时注意要在 plt.show() 前调用 plt.savefig() 基本语法:matplotlib.pyplot.savefig(fname, dpi=None, facecolor='w', edgecolor='w', format=None, transparent=False) 参数 描述 fname str 类型 / 文件路径 / 类似文件的对象如果未设置格式,则根据 fname 的扩展名(如果有)和 rcParams[“savefig.format”] = ‘png’ 推断输出格式如果设置了格式,则它将确定输出格式 dpi 保存图片的像素(dpi),以每英寸点数为单位。如果为 None,则默认取 rcParams[’savefig.dpi’] = ‘figure’ facecolor 保存图片的画布颜色,默认为 white edgecolor 保存图片的边缘颜色,默认为 white format 保存图片的格式,未设置则取 fname 中的格式 transparent 保存图片的背景是否透明 应用举例: 1234567891011121314import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = range(2, 26, 2)y = range(0, 12)a = [5, 10, 15, 20, 25, 30]b = [3, 4, 5, 6, 7, 8]line1, = plt.plot(a, b)line2, = plt.plot(x, y)plt.legend((line1, line2), ('图例一', '图例二'), loc=2, edgecolor='red', facecolor='#F5F5F5')plt.savefig('D:\\\\data\\\\pic.png', transparent=True) # 保存为透明文件plt.show() 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105828143未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Matplotlib","slug":"Python-数据分析/Matplotlib","permalink":"https://www.itrhx.com/categories/Python-数据分析/Matplotlib/"}],"tags":[{"name":"Matplotlib","slug":"Matplotlib","permalink":"https://www.itrhx.com/tags/Matplotlib/"},{"name":"图例","slug":"图例","permalink":"https://www.itrhx.com/tags/图例/"},{"name":"LaTeX","slug":"LaTeX","permalink":"https://www.itrhx.com/tags/LaTeX/"},{"name":"子图","slug":"子图","permalink":"https://www.itrhx.com/tags/子图/"},{"name":"补丁","slug":"补丁","permalink":"https://www.itrhx.com/tags/补丁/"}]},{"title":"Python 数据分析三剑客之 Matplotlib(二):文本描述/中文支持/画布/网格等基本图像属性","slug":"A69-Matplotlib-02","date":"2020-04-12T12:16:52.040Z","updated":"2020-08-06T03:07:49.826Z","comments":true,"path":"2020/04/12/A69-Matplotlib-02/","link":"","permalink":"https://www.itrhx.com/2020/04/12/A69-Matplotlib-02/","excerpt":"","text":"Matplotlib 系列文章: Python 数据分析三剑客之 Matplotlib(一):初识 Matplotlib 与其 matplotibrc 配置文件 Python 数据分析三剑客之 Matplotlib(二):文本描述 / 中文支持 / 画布 / 网格等基本图像属性 Python 数据分析三剑客之 Matplotlib(三):图例 / LaTeX / 刻度 / 子图 / 补丁等基本图像属性 Python 数据分析三剑客之 Matplotlib(四):线性图的绘制 Python 数据分析三剑客之 Matplotlib(五):散点图的绘制 Python 数据分析三剑客之 Matplotlib(六):直方图 / 柱状图 / 条形图的绘制 Python 数据分析三剑客之 Matplotlib(七):饼状图的绘制 Python 数据分析三剑客之 Matplotlib(八):等高线 / 等值线图的绘制 Python 数据分析三剑客之 Matplotlib(九):极区图 / 极坐标图 / 雷达图的绘制 Python 数据分析三剑客之 Matplotlib(十):3D 图的绘制 Python 数据分析三剑客之 Matplotlib(十一):最热门最常用的 50 个图表【译文】 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105828049未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【1x00】添加文本描述【1x01】添加标题:matplotlib.pyplot.title()matplotlib.pyplot.title() 方法可为图表添加标题。 基本语法:matplotlib.pyplot.title(label[, fontdict=None, loc=None, pad=None]) 参数 描述 label str 类型,标题文字 fontdict 字典类型,控制标题文本外观,可选项,默认值为:{'fontsize': rcParams['axes.titlesize'],'fontweight' : rcParams['axes.titleweight'],'color' : rcParams['axes.titlecolor'],'verticalalignment': 'baseline','horizontalalignment': loc} loc str 类型,可选项,三个可选值:center、left、right,默认为 rcParams["axes.titlelocation"](默认为 center) pad float 类型,可选项,标题距轴顶部的偏移量(以磅为单位)。如果为 None,则默认为 rcParams["axes.titlepad"](默认为:6.0) 应用举例: 1234567import matplotlib.pyplot as pltx = range(2, 26, 2)y = range(0, 12)plt.title('This is a title')plt.plot(x, y)plt.show() 【1x02】为坐标轴添加标签:matplotlib.pyplot.xlabel() / ylabel()matplotlib.pyplot.xlabel():为 x 轴添加标签;matplotlib.pyplot.ylabel():为 y 轴添加标签。 基本语法:matplotlib.pyplot.xlabel(xlabel[, fontdict=None, labelpad=None])matplotlib.pyplot.ylabel(ylabel[, fontdict=None, labelpad=None]) 参数 描述 xlabel / ylabel str 类型,要添加的文本信息 fontdict 字典类型,控制标题文本外观,可选项,默认值为:{'fontsize': rcParams['axes.titlesize'],'fontweight' : rcParams['axes.titleweight'],'color' : rcParams['axes.titlecolor'],'verticalalignment': 'baseline','horizontalalignment': loc} labelpad float 类型,可选项,x 轴标签距离 x 轴的距离 应用举例: 123456789101112131415import matplotlib.pyplot as pltx = range(2, 26, 2)y = range(0, 12)a = [5, 10, 15, 20, 25, 30]b = [3, 4, 5, 6, 7, 8]plt.title('This is a title')plt.xlabel('This is x label', fontdict={'fontsize': 15, 'fontweight': 'bold', 'color': 'red'}, labelpad=15.0)plt.ylabel('This is y label', fontsize=10, fontweight='light', color='blue', labelpad=15.0)plt.plot(x, y)plt.plot(a, b)plt.show() 【1x03】任意位置添加文本:matplotlib.pyplot.text()matplotlib.pyplot.text() 方法可以在画布上任意位置添加文本描述。 基本语法:matplotlib.pyplot.text(x, y, s[, fontdict=None, withdash=<deprecated parameter>]) 参数 描述 x, y 放置文本的坐标位置 s str 类型,要添加的文本信息 fontdict 字典类型,控制标题文本外观,可选项,默认值为:{'fontsize': rcParams['axes.titlesize'],'fontweight' : rcParams['axes.titleweight'],'color' : rcParams['axes.titlecolor'],'verticalalignment': 'baseline','horizontalalignment': loc} ha 注释点在注释文本的左边、右边或中间(left、right、center) va 注释点在注释文本的上边、下边、中间或基线 (top、bottom、center、baseline) withdash bool 类型,可选项,默认为 False,创建一个 TextWithDash 实例而不是一个 Text 实例 应用举例: 1234567891011121314151617181920import matplotlib.pyplot as pltplt.rcParams['lines.marker'] = 'o' # 设置线条上点的形状a = [5, 10, 15, 20, 25, 30]b = [3, 4, 5, 6, 7, 8]plt.title('This is a title')plt.xlabel('This is x label')plt.ylabel('This is y label')plt.text(4, 3.2, 'text1')plt.text(9, 4.2, 'text2')plt.text(14, 5.2, 'text3')plt.text(19, 6.2, 'text4')plt.text(24, 7.2, 'text5')plt.text(27.5, 7.9, 'text6')plt.plot(a, b)plt.show() 【1x03】任意位置添加文本:matplotlib.pyplot.annotate()matplotlib.pyplot.annotate() 方法可以在指定坐标点添加文本或 LaTeX 描述,也可以在其他位置添加描述后,使用箭头指向某个坐标点。比 matplotlib.pyplot.text() 更高级。 基本语法:matplotlib.pyplot.annotate(text, xy, xytext, xycoords, textcoords, ha, va, arrowprops, \\*\\*kwargs) 参数 描述 text str 类型,注释的文本 xy 被注释的坐标点,格式:(x, y) xytext 注释文本的坐标点,格式:(x, y),默认与 xy 相同 xycoords 被注释的坐标点的参考系,取值参见表一,默认为 ‘data’ textcoords 注释文本的坐标点的参考系,取值参见表二,默认为 xycoords 的值 ha 注释点在注释文本的左边、右边或中间(left、right、center) va 注释点在注释文本的上边、下边、中间或基线 (top、bottom、center、baseline) arrowprops dict 字典类型,箭头的样式如果 arrowprops 不包含键 arrowstyle,则允许的键参见表三如果 arrowprops 包含键 arrowstyle,则允许的键参见表四 表一:xycoords 取值类型 取值 描述 ‘figure points’ 以画布左下角为参考,单位为点数 ‘figure pixels’ 以画布左下角为参考,单位为像素 ‘figure fraction’ 以画布左下角为参考,单位为百分比 ‘axes points’ 以绘图区左下角为参考,单位为点数 ‘axes pixels’ 以绘图区左下角为参考,单位为像素 ‘axes fraction’ 以绘图区左下角为参考,单位为百分比 ‘data’ 使用被注释对象的坐标系,即数据的 x, y 轴(默认) ‘polar’ 使用(θ,r)形式的极坐标系 表二:textcoords 取值类型 取值 描述 ‘figure points’ 以画布左下角为参考,单位为点数 ‘figure pixels’ 以画布左下角为参考,单位为像素 ‘figure fraction’ 以画布左下角为参考,单位为百分比 ‘axes points’ 以绘图区左下角为参考,单位为点数 ‘axes pixels’ 以绘图区左下角为参考,单位为像素 ‘axes fraction’ 以绘图区左下角为参考,单位为百分比 ‘data’ 使用被注释对象的坐标系,即数据的 x, y 轴 ‘polar’ 使用(θ,r)形式的极坐标系 ‘offset points’ 相对于被注释点的坐标 xy 的偏移量,单位是点 ‘offset pixels’ 相对于被注释点的坐标 xy 的偏移量,单位是像素 表三:arrowprops 不包含键 arrowstyle 时的取值 键 描述 width 箭头的宽度,以点为单位 headwidth 箭头底部的宽度,以点为单位 headlength 箭头的长度,以点为单位 shrink 箭头两端收缩占总长的百分比 ? 其他 matplotlib.patches.FancyArrowPatch 中的关键字,部分常用关键字参见表五 表四:arrowprops 包含键 arrowstyle 时的取值 取值 描述 '-' None '->' head_length=0.4,head_width=0.2 '-[' widthB=1.0,lengthB=0.2,angleB=None ']-' widthA=1.0, lengthA=0.2, angleA=None ]-[ widthA=1.0, lengthA=0.2, angleA=None, widthB=1.0, lengthB=0.2, angleB=None `’ - ‘` widthA=1.0,widthB=1.0 `’- >’` head_length=0.4,head_width=0.2 '<-' head_length=0.4,head_width=0.2 '<->' head_length=0.4,head_width=0.2 `’< -‘` head_length=0.4,head_width=0.2 `’< - >’` head_length=0.4,head_width=0.2 'fancy' head_length=0.4,head_width=0.4,tail_width=0.4 'simple' head_length=0.5,head_width=0.5,tail_width=0.2 'wedge' tail_width=0.3,shrink_factor=0.5 表五:matplotlib.patches.FancyArrowPatch 常用的键 键 描述 arrowstyle 箭头样式,取值参见表四 connectionstyle 连接方式,默认为 arc3,有以下五种取值:angle:angleA=90, angleB=0, rad=0.0angle3:angleA=90, angleB=0arc:angleA=0, angleB=0, armA=None, armB=None, rad=0.0arc3:rad=0.0bar:armA=0.0, armB=0.0, fraction=0.3, angle=Noneangle 为箭头旋转的角度,rad 为弧度 relpos 箭头起始点相对注释文本的位置,默认为 (0.5, 0.5),即文本的中心(0,0)表示左下角,(1,1)表示右上角 patchA 箭头起点处的图形,默认为文本的边框 patchB 箭头终点处的图形,默认为空 shrinkA 箭头起点的缩进点数,默认为2 shrinkB 箭头终点的缩进点数,默认为2 ? 其他键值,参见官方文档 matplotlib.patches.PathPatch connectionstyle 样式举例 应用举例: 1234567891011121314151617181920import numpy as npimport matplotlib.pyplot as pltx = np.arange(-2*np.pi, 2*np.pi, 0.01)y = np.sin(1*x)/xplt.title('This is a title')plt.xlabel('This is x label')plt.ylabel('This is y label')plt.plot(x, y)plt.annotate(r'$\\lim_{x\\to 0}\\frac{\\sin(x)}{x}=1$', # 插入 LaTeX 表达式 xy=[0, 1], # 被标记的坐标 xycoords='data', # 被标记的坐标的参考系 xytext=[50, -40], # 注释文本的坐标 textcoords='offset points', # 注释文本的坐标的参考系 fontsize=16, # 字体大小 arrowprops=dict(arrowstyle=\"->\", connectionstyle=\"arc3, rad=.2\")) # 箭头样式plt.show() 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105828049未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【2x00】设置中文显示【2x01】常见系统自带文字及其英文名称Windows 系统中常见自带字体: 字体 英文名称 黑体 SimHei 宋体 SimSun 新宋体 NSimSun 仿宋 FangSong 仿宋_GB2312 FangSong_GB2312 楷体_GB2312 KaiTi_GB2312 楷体 KaiTi 微软正黑 Microsoft JhengHei 微软雅黑 Microsoft YaHei 细明体 MingLiU 标楷体 DFKai-SB 新细明体 PMingLiU 装有 office 后新添加的字体: 字体 英文名称 隶书 LiSu 幼圆 YouYuan 华文细黑 STXihei 华文楷体 STKaiti 华文宋体 STSong 华文中宋 STZhongsong 华文仿宋 STFangsong 方正舒体 FZShuTi 方正姚体 FZYaoti 华文彩云 STCaiyun 华文琥珀 STHupo 华文隶书 STLiti 华文行楷 STXingkai 华文新魏 STXinwei Mac OS 系统中常见自带字体: 字体 英文名称 华文细黑 STHeiti Light / STXihei 华文黑体 STHeiti 华文楷体 STKaiti 华文宋体 STSong 华文仿宋 STFangsong 丽黑 Pro LiHei Pro Medium 丽宋 Pro LiSong Pro Light 标楷体 BiauKai 苹果丽中黑 Apple LiGothic Medium 苹果丽细宋 Apple LiSung Light 【2x02】指定全局字体:rcParams通过 rcParams['font.sans-serif'] 可以配置全局字体。 优点:只需设置一次即可显示所有中文;缺点:污染全局,无法对单个中文设置字体。 应用举例: 12345678910111213import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei'] # 配置全局字体为微软雅黑plt.rcParams['axes.unicode_minus'] = False # 部分字体负号会显示乱码,可添加此参数进行配置a = [-15, -10, -5, 20, 25, 30]b = [-5, -4, -3, 6, 7, 8]plt.title('这是中文标题')plt.xlabel('这是 x 轴标签')plt.ylabel('这是 y 轴标签')plt.plot(a, b)plt.show() 【2x03】指定单个字体:fontpropertiesfontproperties 参数可以加在要设置中文的地方 优点:不污染全局;缺点:中文太多了挨个设置比较繁琐。 应用举例: 1234567891011import matplotlib.pyplot as plta = [-15, -10, -5, 20, 25, 30]b = [-5, -4, -3, 6, 7, 8]plt.title('这是中文标题', fontproperties='Microsoft JhengHei') # 微软正黑plt.xlabel('这是 x 轴标签', fontproperties='STLiti') # 华文隶书plt.ylabel('这是 y 轴标签', fontproperties='Microsoft YaHei') # 微软雅黑plt.plot(a, b)plt.show() 【2x04】指定文字路径:FontPropertiesmatplotlib 中 font_manager 模块的 FontProperties 方法可以通过指定文字路径来使用本地文字,在 Windows 中,文字路径一般是 C:\\Windows\\Fonts\\,文字名称可以通过其属性来获取,部分用户自己安装的字体可能包含多个类型,可打开字体合集后通过其属性来获取。 12345678910111213import matplotlib.pyplot as pltfrom matplotlib.font_manager import FontPropertiesfont = FontProperties(fname=r\"C:\\Windows\\Fonts\\STXINGKA.TTF\", size=14)a = [-15, -10, -5, 20, 25, 30]b = [-5, -4, -3, 6, 7, 8]plt.title('这是中文标题', fontproperties=font)plt.xlabel('这是 x 轴标签', fontproperties=font)plt.ylabel('这是 y 轴标签', fontproperties=font)plt.plot(a, b)plt.show() 【2x05】文字更多属性:rcrc 参数支持文字的更多属性设置,如字体粗细、大小等,这种方法同样将影响全局。 官方参考:https://matplotlib.org/api/matplotlib_configuration_api.html?highlight=rc#matplotlib.rc 应用举例: 1234567891011121314151617import matplotlib.pyplot as pltfont = {'family': 'SimHei', 'weight': 'bold', 'size': '10'}plt.rc('font', **font) # 设置字体的更多属性plt.rc('axes', unicode_minus=False) # 显示负号a = [-15, -10, -5, 20, 25, 30]b = [-5, -4, -3, 6, 7, 8]plt.title('这是中文标题')plt.xlabel('这是 x 轴标签')plt.ylabel('这是 y 轴标签')plt.plot(a, b)plt.show() 【3x00】设置画布大小 / 分辨率 / 颜色matplotlib.pyplot.figure() 可以设置画布的大小、图片分辨率、颜色等。 基本语法:matplotlib.pyplot.figure(figsize=None, dpi=None, facecolor=None, edgecolor=None, frameon=True, \\*\\*kwargs) 参数 描述 figsize (float, float) 的格式,代表宽度和高度,单位为英寸默认为 rcParams["figure.figsize"] = [6.4, 4.8],即:640 x 480 dpi 图像分辨率,默认为 rcParams["figure.figsize"] = 100 facecolor 图像背景颜色,默认为 rcParams["figure.edgecolor"] = ‘white’ edgecolor 图像边缘颜色,默认为 rcParams[’figure.edgecolor’] = ‘white’ frameon 是否启用图框 应用举例: 12345678910import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']x = range(2, 26, 2)y = range(0, 12)plt.figure(figsize=(6.5, 5), dpi=120, facecolor='#BBFFFF')plt.plot(x, y)plt.show() 【4x00】设置网格matplotlib.pyplot.grid() 方法可以为图表设置网格显示。 基本语法:matplotlib.pyplot.grid([b=None, which='major', axis='both', \\*\\*kwargs]) 参数 属性 b bool 值,可选项,是否显示网格,值为 None 或 True 则显示,False 不显示 which 可选项,在主/次刻度显示网格线,major:主(大)刻度;minor:次(小)刻度;both:两者同时显示 axis 可选项,在横/竖轴显示网格线,x:x 轴;y:y 轴;both:两者同时显示 **kwargs 其他 Line2D 属性,常见 Line2D 属性见下表 Line2D 属性用法:grid(color='r', linestyle='-', linewidth=2),部分常见 Line2D 属性如下: 属性 描述 alpha 网格透明度,float 类型,取值范围:[0, 1],默认为 1.0,即不透明 antialiased / aa 是否使用抗锯齿渲染,默认为 True color / c 网格颜色,支持英文颜色名称及其简写、十六进制颜色码等,更多颜色示例参见官网 Color Demo linestyle / ls 网格线条样式:'-' or 'solid', '--' or 'dashed', '-.' or 'dashdot' ':' or 'dotted', 'none' or ' ' or '' linewidth / lw 网格线条宽度,float 类型,默认 0.8 应用举例: 12345678910111213import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei']a = [-15, -10, -5, 20, 25, 30]b = [-5, -4, -3, 6, 7, 8]plt.title('这是中文标题')plt.xlabel('这是 x 轴标签')plt.ylabel('这是 y 轴标签')plt.grid(axis='x', color='red', linestyle='-.', linewidth=2)plt.plot(a, b)plt.show() 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105828049未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Matplotlib","slug":"Python-数据分析/Matplotlib","permalink":"https://www.itrhx.com/categories/Python-数据分析/Matplotlib/"}],"tags":[{"name":"Matplotlib","slug":"Matplotlib","permalink":"https://www.itrhx.com/tags/Matplotlib/"},{"name":"画布","slug":"画布","permalink":"https://www.itrhx.com/tags/画布/"},{"name":"网格","slug":"网格","permalink":"https://www.itrhx.com/tags/网格/"}]},{"title":"Python 数据分析三剑客之 Matplotlib(一):初识 Matplotlib 与其 Matplotibrc 配置文件","slug":"A68-Matplotlib-01","date":"2020-04-10T12:07:49.148Z","updated":"2020-08-06T03:05:37.600Z","comments":true,"path":"2020/04/10/A68-Matplotlib-01/","link":"","permalink":"https://www.itrhx.com/2020/04/10/A68-Matplotlib-01/","excerpt":"","text":"Matplotlib 系列文章: Python 数据分析三剑客之 Matplotlib(一):初识 Matplotlib 与其 matplotibrc 配置文件 Python 数据分析三剑客之 Matplotlib(二):文本描述 / 中文支持 / 画布 / 网格等基本图像属性 Python 数据分析三剑客之 Matplotlib(三):图例 / LaTeX / 刻度 / 子图 / 补丁等基本图像属性 Python 数据分析三剑客之 Matplotlib(四):线性图的绘制 Python 数据分析三剑客之 Matplotlib(五):散点图的绘制 Python 数据分析三剑客之 Matplotlib(六):直方图 / 柱状图 / 条形图的绘制 Python 数据分析三剑客之 Matplotlib(七):饼状图的绘制 Python 数据分析三剑客之 Matplotlib(八):等高线 / 等值线图的绘制 Python 数据分析三剑客之 Matplotlib(九):极区图 / 极坐标图 / 雷达图的绘制 Python 数据分析三剑客之 Matplotlib(十):3D 图的绘制 Python 数据分析三剑客之 Matplotlib(十一):最热门最常用的 50 个图表【译文】 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105638122未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【1x00】认识 MatplotlibMatplotlib 是建立在 NumPy 数组基础上的多平台数据可视化程序库,用于在 Python 中绘制数组的 2D 图形库,最初被设计用于完善 SciPy 的生态环境,虽然它起源于模仿 Matlab 图形命令,但它独立于 Matlab,可以以 Pythonic 和面向对象的方式使用。虽然 Matplotlib 主要是在纯 Python 中编写的,但它大量使用 NumPy 和其他扩展代码,即使对于大型数组也能提供良好的性能。它与 NumPy 一起使用,提供了一种有效的 Matlab 开源替代方案。 它也可以和图形工具包一起使用,如 PyQt 和 wxPython。Matplotlib 最重要的特性之一就是具有良好的操作系统兼容性和图形显示底层接口兼容性。 【1x01】简单示例123456>>> import matplotlib.pyplot as plt>>> x = range(2, 26, 2) # 数据在 x 轴的位置,是一个可迭代对象>>> y = range(0, 12) # 数据在 y 轴的位置,是一个可迭代对象>>> plt.plot(x, y) # 绘制线形图[<matplotlib.lines.Line2D object at 0x00BA1D18>]>>> plt.show() 【1x02】图像结构 【1x03】三层结构Matplotlib 三层结构:容器层、辅助显示层、图像层 容器层 容器层主要由 Canvas、Figure、Axes 组成。 Canvas 是位于最底层的系统层,在绘图的过程中充当画板的角色,即放置画布(Figure)的工具。 Figure 是 Canvas 上方的第一层,也是需要用户来操作的应用层的第一层,在绘图的过程中充当画布的角色,可以通过 plt.figure() 设置画布的大小和分辨率等 Axes 是应用层的第二层,在绘图的过程中相当于画布上的绘图区的角色,注意与 Axis 的区别,Axis 是坐标轴,包含大小限制、刻度和刻度标签。 注意点: 一个figure(画布)可以包含多个axes(坐标系/绘图区),但是一个 axes 只能属于一个figure。 一个axes(坐标系/绘图区)可以包含多个axis(坐标轴),包含两个即为 2d 坐标系,三个即为 3d 坐标系 。 辅助显示层 辅助显示层为 Axes(绘图区)内的除了根据数据绘制出的图像以外的内容,主要包括 Axes 外观(facecolor)、边框线(spines)、坐标轴(axis)、坐标轴名称(axis label)、坐标轴刻度(tick)、坐标轴刻度标签(tick label)、网格线(grid)、图例(legend)、标题(title)等内容。该层的设置可使图像显示更加直观更加容易被用户理解,但又不会对图像产生实质的影响。 图像层 图像层指 Axes 内通过 plot(线形图)、scatter(散点图)、bar(柱状图)、histogram(直方图)、pie(饼图) 等函数根据数据绘制出的图像。 三者关系总结 Canvas(画板)位于最底层,用户一般接触不到; Figure(画布)建立在 Canvas 之上; Axes(绘图区)建立在Figure之上; 坐标轴(axis)、图例(legend)等辅助显示层以及图像层都是建立在 Axes 之上。 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105638122未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【2x00】了解 matplotlib.rcParamsmatplotlib 使用 matplotlibrc 配置文件来自定义图形的各种默认属性,称之为 rc 配置或 rc 参数。通过 rc 参数可以修改默认的属性,包括窗体大小、每英寸的点数、线条宽度、颜色、样式、坐标轴、坐标和网络属性、文本、字体等。rc 参数存储在字典变量中,通过字典的方式进行访问。 执行 matplotlib.rcParams.keys() 命令可以查看所有的 rc 参数及其默认值;执行 matplotlib.matplotlib_fname() 命令可以查看 matplotlibrc 配置文件在本地的路径。 官网介绍:https://matplotlib.org/tutorials/introductory/customizing.html 配置文件 matplotibrc 主要包括以下配置要素: axes:坐标轴的背景颜色、坐标轴的边缘颜色、刻度线的大小、刻度标签的字体大小等; figure:画布标题大小、画布标题粗细、画布像素(dpi)、 画布背景颜色和边缘颜色等; font:字体类别、字体风格、字体粗细和字体大小等; grid:网格颜色、网格线条风格、网格线条宽度和网格透明度; legend:图例的文本大小、阴影、图例线框风格等; lines:设置线条属性,包括颜色、线条风格、线条宽度和标记风格等; patch:填充 2D 空间的图形对象,包括多边形和圆; savefig:保存画布图像的分辨率、背景颜色和边缘颜色等; text:文本颜色、LaTex 渲染文本等; xtick / ytick:x 轴和 y 轴的主次要刻度线的大小、宽度、刻度线颜色和刻度标签大小等。 我们可以在 Python 项目中动态设置 rc 参数,所有 rc 参数设置都存储在名为 matplotlib.rcParams 的类似于字典的变量中,该变量对于 Matplotlib 软件包是全局的。rcParams 可以直接修改。通过这种方法的修改会对全局产生影响,在 Matplotlib 的其他方法中也可以单独对某个参数进行修改,后续介绍不同方法时会见到。 rcParams 修改示例: 123456789101112131415161718192021import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Microsoft YaHei'] # 定义全局字体plt.rcParams['xtick.color'] = 'red' # 定义 x 轴刻度颜色plt.rcParams['lines.marker'] = 'o' # 定义线条上点的形状plt.rcParams['legend.loc'] = 'upper left' # 定义图例在左上角x = range(2, 26, 2)y = range(0, 12)a = [5, 10, 15, 20, 25, 30]b = [3, 4, 5, 6, 7, 8]plt.title('This is a title / 这是标题')plt.xlabel('这是 x 轴标题')plt.ylabel('这是 y 轴标题')plt.grid(True)plt.plot(x, y)plt.plot(a, b)plt.legend(['图例一', '图例二'])plt.show() 【2x01】axes 部分属性 属性及其默认值 描述 其他取值 mpl.rcParams[‘axes.axisbelow‘] = ‘line’ 网格线和刻度的位置 line:在画板上方,在线条下方False:在线条和画板的上方True:在画板下方 mpl.rcParams[‘axes.edgecolor‘] = ‘black’ 轴边缘颜色 其他颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo mpl.rcParams[‘axes.facecolor‘] = ‘white’ 轴背景色 其他颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo mpl.rcParams[‘axes.labelcolor‘] = ‘black’ 轴标题颜色 其他颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo mpl.rcParams[‘axes.grid‘] = False 是否显示网格 False:不显示网格;True:显示网格 mpl.rcParams[‘axes.grid.axis‘] = ‘both’ 网格应用于哪个轴 x:x 轴;y:y 轴;both:同时应用于两个轴 mpl.rcParams[‘axes.grid.which‘] = ‘major’ 网格应用于哪个刻度 major:主(大)刻度;minor:次(小刻度);both:同时应用于两个刻度 mpl.rcParams[‘axes.labelpad‘] = 4.0 轴标题和轴之间的间距 float 类型间距值 mpl.rcParams[‘axes.labelsize‘] = ‘medium’ x 轴和 y 轴标题的字体大小 xx-small, x-small, small, medium large, x-large, xx-large, smaller, larger也可以使用数字来表示字体大小 mpl.rcParams[‘axes.labelweight‘] = ‘normal’ x 轴和 y 轴标题的字体粗细 normal:正常粗细;bold:粗体;light:细体数字值 400 等价于 normal,700 等价于 bold mpl.rcParams[‘axes.linewidth‘] = 0.8 轴边线宽度 float 类型宽度值 mpl.rcParams[‘axes.titlecolor‘] = ‘auto’ 图表标题颜色 默认取 text.color 的值其他颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo mpl.rcParams[‘axes.titlelocation‘] = ‘center’ 图表标题位置 left:左;right:右;center:中间 mpl.rcParams[‘axes.titlepad‘] = 6.0 图表标题和轴之间的间距 float 类型间距值 mpl.rcParams[‘axes.titlesize‘] = ‘large’ 图表标题字体大小 xx-small, x-small, small, medium large, x-large, xx-large, smaller, larger也可以使用数字来表示字体大小 mpl.rcParams[‘axes.titleweight‘] = ‘normal’ 图表标题字体粗细 normal:正常粗细;bold:粗体;light:细体数字值 400 等价于 normal,700 等价于 bold mpl.rcParams[‘axes.xmargin‘] = 0.05 x 轴边距 取值范围 [0, 1] mpl.rcParams[‘axes.ymargin‘] = 0.05 y 轴边距 取值范围 [0, 1] mpl.rcParams[‘axes.unicode_minus‘] = True 对负号使用 Unicode 而不是连字符 True:是;False:否 mpl.rcParams[‘axes3d.grid‘] = True 是否在三维轴上显示网格 True:是;False:否 【2x02】figure 部分属性 属性及其默认值 描述 其他取值 mpl.rcParams[‘figure.dpi‘] = 100 画布像素(dpi) float 类型像素值 mpl.rcParams[‘figure.edgecolor‘] = ‘white’ 画布边缘颜色 其他颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo mpl.rcParams[‘figure.facecolor‘] = ‘white’ 画布背景颜色 其他颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo mpl.rcParams[‘figure.figsize‘] = [6.4, 4.8] 画布尺寸 [长, 宽] float 类型尺寸值(英寸) mpl.rcParams[‘figure.frameon‘] = True 是否启用图框 True:是;False:否 mpl.rcParams[‘figure.titlesize‘] = ‘large’ 画布标题大小 xx-small, x-small, small, medium large, x-large, xx-large, smaller, larger也可以使用数字来表示字体大小 mpl.rcParams[‘figure.titleweight‘] = ‘normal’ 画布标题粗细 normal:正常粗细;bold:粗体;light:细体数字值 400 等价于 normal,700 等价于 bold 【2x03】font 部分属性 属性及其默认值 描述 其他取值 mpl.rcParams[‘font.family‘] = [‘sans-serif’] 规定字体系列 字体名称 mpl.rcParams[‘font.sans-serif‘] = [‘DejaVu Sans, ……’] 定义无衬线字体 默认是一些西文字体,可将其设置成其他字体来显示中文 mpl.rcParams[‘font.serif‘] = [‘DejaVu Sans, ……’] 定义有衬线字体 默认是一些西文字体,可将其设置成其他字体来显示中文 mpl.rcParams[‘font.size‘] = 10.0 定义字体大小 float 数字类型字体大小 mpl.rcParams[‘font.weight‘] = ‘normal’ 定义字体粗细 normal:正常粗细;bold:粗体;light:细体数字值 400 等价于 normal,700 等价于 bold 【2x04】grid 部分属性 属性及其默认值 描述 其他取值 mpl.rcParams[‘grid.alpha‘] = 1.0 网格透明度 float 类型,取值范围:[0, 1] mpl.rcParams[‘grid.color‘] = ‘#b0b0b0’ 网格颜色 其他颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo mpl.rcParams[‘grid.linestyle‘] = ‘-‘ 网格线的样式 '-' or 'solid', '--' or 'dashed', '-.' or 'dashdot' ':' or 'dotted', 'none' or ' ' or '' mpl.rcParams[‘grid.linewidth‘] = 0.8 网格宽度 float 类型宽度值 【2x05】legend 部分属性 属性及其默认值 描述 其他取值 mpl.rcParams[‘legend.borderaxespad‘] = 0.5 图例距离轴之间的距离 float 类型距离值 mpl.rcParams[‘legend.borderpad‘] = 0.4 图例边框空白区域大小 float 类型大小值 mpl.rcParams[‘legend.columnspacing‘] = 2.0 图例列间距 float 类型距离值 mpl.rcParams[‘legend.edgecolor‘] = 0.8 图例边缘线颜色 其他颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo mpl.rcParams[‘legend.facecolor‘] = ‘inherit’ 图例背景颜色 默认继承自 axes.facecolor其他颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo mpl.rcParams[‘legend.fancybox‘] = True 是否使用圆形框作为图例背景 True:使用圆形框;False:使用矩形框 mpl.rcParams[‘legend.fontsize‘] = ‘medium’ 图例字体大小 xx-small, x-small, small, medium large, x-large, xx-large, smaller, larger也可以使用数字来表示字体大小 mpl.rcParams[‘legend.framealpha‘] = 0.8 图例透明度 float 类型,取值范围:[0, 1] mpl.rcParams[‘legend.frameon‘] = True 是否在画布之上绘制图例 True:是;False:否 mpl.rcParams[‘legend.handleheight‘] = 0.7 图例的高度 float 类型高度值 mpl.rcParams[‘legend.handlelength‘] = 2.0 图例的宽度 float 类型宽度值 mpl.rcParams[‘legend.handletextpad‘] = 0.8 图例和图例文本之间的水平距离 float 类型距离值 mpl.rcParams[‘legend.labelspacing‘] = 0.5 不同图例之间的垂直距离 float 类型距离值 mpl.rcParams[‘legend.loc‘] = ‘best’ 图例在画布中的位置 best, upper right, upper left, lower left lower right, right, center left, center right lower center, upper center, center mpl.rcParams[‘legend.shadow‘] = False 是否给图例添加阴影效果 True:是;False:否 【2x06】lines 部分属性 属性及其默认值 描述 其他取值 mpl.rcParams[‘lines.antialiased‘] = True 是否以抗锯齿方式渲染线条 True:是;False:否 mpl.rcParams[‘lines.color‘] = ‘C0’ 线条颜色(对 plot() 没有影响) 其他颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo mpl.rcParams[‘lines.linestyle‘] = ‘-‘ 线条样式 '-', '--', '-.', ':', 'solid', 'dashed', 'dashdot', 'dotted', 'none', ' ', '' mpl.rcParams[‘lines.linewidth‘] = 1.5 线条宽度 float 类型宽度值 mpl.rcParams[‘lines.marker‘] = ‘None’ 线条上点的形状 ., ,, o, v, ^ 等,具体常见 matplotlib.markers mpl.rcParams[‘lines.markeredgecolor‘] = ‘auto’ 线条上点边缘的颜色 其他颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo mpl.rcParams[‘lines.markerfacecolor‘] = ‘auto’ 线条上点的颜色 其他颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo mpl.rcParams[‘lines.markeredgewidth‘] = 1.0 线条上点的粗细 float 类型粗细值 mpl.rcParams[‘lines.markersize‘] = 6.0 线条上点的大小 float 类型大小值 【2x07】patch 部分属性 属性及其默认值 描述 其他取值 mpl.rcParams[‘patch.antialiased‘] = True 以抗锯齿方式渲染补丁 True:是;False:否 mpl.rcParams[‘patch.edgecolor‘] = ‘black’ 补丁边缘颜色 其他颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo mpl.rcParams[‘patch.facecolor‘] = ‘C0’ 补丁颜色 其他颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo mpl.rcParams[‘patch.linewidth‘] = 1.0 补丁边缘宽度(以磅为单位) float 类型宽度值 【2x08】savefig 部分属性 属性及其默认值 描述 其他取值 mpl.rcParams[‘savefig.bbox‘] = None 是否以紧凑形式保存图片 standard:标准形式;tight:紧凑形式(去掉边上多余的空白) mpl.rcParams[‘savefig.pad_inches‘] = 0.1 savefig.bbox 参数为 tight 时,图片使用的填充值(相当于 html 中的 Padding) float 类型填充值 mpl.rcParams[‘savefig.dpi‘] = ‘figure’ 保存图片的像素(dpi) str 类型像素值 mpl.rcParams[‘savefig.edgecolor‘] = ‘white’ 保存图片的边缘颜色 其他颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo mpl.rcParams[‘savefig.facecolor‘] = ‘white’ 保存图片的画布颜色 其他颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo mpl.rcParams[‘savefig.format‘] = ‘png’ 保存图片的格式 eps, pdf, pgf, png, ps, raw, rgba, svg, svgz mpl.rcParams[‘savefig.transparent‘] = False 保存图片的背景是否透明 True:是;False:否 【2x09】text 部分属性 属性及其默认值 描述 其他取值 mpl.rcParams[‘text.antialiased‘] = True 是否以抗锯齿方式渲染文本 True:是;False:否 mpl.rcParams[‘text.color‘] = ‘red’ 文本颜色 其他颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo mpl.rcParams[‘text.usetex‘] = False 是否使用 LaTeX 排版系统(主要用于生成复杂表格和数学公式) True:是;False:否 【2x10】xtick 部分属性 属性及其默认值 描述 其他取值 mpl.rcParams[‘xtick.color‘] = ‘black’ x 轴刻度的颜色 其他颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo mpl.rcParams[‘xtick.direction‘] = ‘out’ x 轴刻度的方向 in:内部(x 轴上方);out:外部(x 轴下方)inout:同时在内部和外部 mpl.rcParams[‘xtick.bottom‘] = True 是否在画布底部显示 x 轴刻度 True:是;False:否 mpl.rcParams[‘xtick.top‘] = False 是否在画布顶部显示 x 轴刻度 True:是;False:否 mpl.rcParams[‘xtick.labelbottom‘] = True 是否在画布底部显示 x 轴刻度文字标签 True:是;False:否 mpl.rcParams[‘xtick.labeltop‘] = False 是否在画布顶部显示 x 轴刻度文字标签 True:是;False:否 mpl.rcParams[‘xtick.labelsize‘] = ‘medium’ x 轴刻度文字大小 xx-small, x-small, small, medium large, x-large, xx-large, smaller, larger也可以使用数字来表示字体大小 mpl.rcParams[‘xtick.major.bottom‘] = True 是否在画布底部显示 x 轴主(大)刻度 True:是;False:否 mpl.rcParams[‘xtick.major.top‘] = True 是否在画布顶部显示 x 轴主(大)刻度 True:是;False:否 mpl.rcParams[‘xtick.major.pad‘] = 3.5 x 轴主(大)刻度与文字标签的距离 float 类型距离值 mpl.rcParams[‘xtick.major.size‘] = 3.5 x 轴主(大)刻度的大小 float 类型大小值 mpl.rcParams[‘xtick.major.width‘] = 0.8 x 轴主(大)刻度的宽度 float 类型宽度值 mpl.rcParams[‘xtick.minor.bottom‘] = True 是否在画布底部显示 x 轴次(小)刻度 True:是;False:否 mpl.rcParams[‘xtick.minor.top‘] = True 是否在画布顶部显示 x 轴次(小)刻度 True:是;False:否 mpl.rcParams[‘xtick.minor.pad‘] = 3.4 x 轴次(小)刻度与文字标签的距离 float 类型距离值 mpl.rcParams[‘xtick.minor.size‘] = 2.0 x 轴次(小)刻度的大小 float 类型大小值 mpl.rcParams[‘xtick.minor.width‘] = 0.6 x 轴次(小)刻度的宽度 float 类型宽度值 mpl.rcParams[‘xtick.minor.visible‘] = False x 轴次(小)刻度的可见性 True:是;False:否 【2x11】ytick 部分属性 属性及其默认值 描述 其他取值 mpl.rcParams[‘ytick.color‘] = ‘black’ y 轴刻度的颜色 其他颜色,支持英文颜色名称及其简写、十六进制颜色码等更多颜色示例参见官网 Color Demo mpl.rcParams[‘ytick.direction‘] = ‘out’ y 轴刻度的方向 in:内部(y 轴右方);out:外部(y 轴左方)inout:同时在内部和外部 mpl.rcParams[‘ytick.left‘] = True 是否在画布左边显示 y 轴刻度 True:是;False:否 mpl.rcParams[‘ytick.right‘] = False 是否在画布右边显示 y 轴刻度 True:是;False:否 mpl.rcParams[‘ytick.labelleft‘] = True 是否在画布左边显示 y 轴刻度文字标签 True:是;False:否 mpl.rcParams[‘ytick.labelright‘] = False 是否在画布右边显示 y 轴刻度文字标签 True:是;False:否 mpl.rcParams[‘ytick.labelsize‘] = ‘medium’ y 轴刻度文字大小 xx-small, x-small, small, medium large, x-large, xx-large, smaller, larger也可以使用数字来表示字体大小 mpl.rcParams[‘ytick.major.left‘] = True 是否在画布左边显示 y 轴主(大)刻度 True:是;False:否 mpl.rcParams[‘ytick.major.right‘] = True 是否在画布右边显示 y 轴主(大)刻度 True:是;False:否 mpl.rcParams[‘ytick.major.pad‘] = 3.5 y 轴主(大)刻度与文字标签的距离 float 类型距离值 mpl.rcParams[‘ytick.major.size‘] = 3.5 y 轴主(大)刻度的大小 float 类型大小值 mpl.rcParams[‘ytick.major.width‘] = 0.8 y 轴主(大)刻度的宽度 float 类型宽度值 mpl.rcParams[‘ytick.minor.left‘] = True 是否在画布左边显示 y 轴次(小)刻度 True:是;False:否 mpl.rcParams[‘ytick.minor.right‘] = True 是否在画布右边显示 y 轴次(小)刻度 True:是;False:否 mpl.rcParams[‘ytick.minor.pad‘] = 3.4 y 轴次(小)刻度与文字标签的距离 float 类型距离值 mpl.rcParams[‘ytick.minor.size‘] = 2.0 y 轴次(小)刻度的大小 float 类型大小值 mpl.rcParams[‘ytick.minor.width‘] = 0.6 y 轴次(小)刻度的宽度 float 类型宽度值 mpl.rcParams[‘ytick.minor.visible‘] = False y 轴次(小)刻度的可见性 True:是;False:否 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105638122未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"Matplotlib","slug":"Python-数据分析/Matplotlib","permalink":"https://www.itrhx.com/categories/Python-数据分析/Matplotlib/"}],"tags":[{"name":"Matplotlib","slug":"Matplotlib","permalink":"https://www.itrhx.com/tags/Matplotlib/"},{"name":"matplotibrc","slug":"matplotibrc","permalink":"https://www.itrhx.com/tags/matplotibrc/"}]},{"title":"Python 数据分析三剑客之 NumPy(六):矩阵 / 线性代数库与 IO 操作","slug":"A67-NumPy-06","date":"2020-03-30T07:23:44.601Z","updated":"2020-07-06T13:28:17.565Z","comments":true,"path":"2020/03/30/A67-NumPy-06/","link":"","permalink":"https://www.itrhx.com/2020/03/30/A67-NumPy-06/","excerpt":"","text":"NumPy 系列文章: Python 数据分析三剑客之 NumPy(一):理解 NumPy / 数组基础 Python 数据分析三剑客之 NumPy(二):数组索引 / 切片 / 广播 / 拼接 / 分割 Python 数据分析三剑客之 NumPy(三):数组的迭代与位运算 Python 数据分析三剑客之 NumPy(四):字符串函数总结与对比 Python 数据分析三剑客之 NumPy(五):数学 / 算术 / 统计 / 排序 / 条件 / 判断函数合集 Python 数据分析三剑客之 NumPy(六):矩阵 / 线性代数库与 IO 操作 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105511641未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【1x00】NumPy 矩阵库numpy.matlib 模块是 NumPy 的矩阵库,该矩阵库包含多种函数,函数返回的是一个矩阵,而不是 Ndarray 对象。 官方文档介绍:https://numpy.org/doc/1.18/reference/routines.matlib.html 【1x01】numpy.mat()numpy.mat() 函数将输入数组转换为为矩阵。 基本语法:numpy.mat(data[, dtype=None]) 参数 描述 data 输入数据,如果 data 为字符串,则需要用逗号或空格分隔列,用分号分隔行 dtype 输出矩阵的数据类型,可选项 应用举例: 12345678>>> import numpy as np>>> a = np.mat([1, 2, 3])>>> amatrix([[1, 2, 3]])>>> a[0]matrix([[1, 2, 3]])>>> a[0,1]2 123456789>>> import numpy as np>>> a = np.array([[1, 2], [3, 4]])>>> aarray([[1, 2], [3, 4]])>>> b = np.mat(a)>>> bmatrix([[1, 2], [3, 4]]) 【1x02】numpy.asmatrix()numpy.asmatrix() 函数将输入数组转换为为矩阵。 基本语法:numpy.asmatrix(data[, dtype=None]) 参数 描述 data 输入数据,如果 data 为字符串,则需要用逗号或空格分隔列,用分号分隔行 dtype 输出矩阵的数据类型,可选项 应用举例: 12345>>> import numpy as np>>> a = np.array([[1, 2], [3, 4]])>>> np.asmatrix(a)matrix([[1, 2], [3, 4]]) 【1x03】numpy.matrix()numpy.matrix() 函数从类似数组的对象或数据字符串中返回一个矩阵。 注意:此函数已经不建议再使用,在未来的版本当中可能会被删除。 基本语法:class numpy.matrix(data[, dtype=None, copy=True]) 参数 描述 data 数组或者字符串,如果 data 为字符串,则需要用逗号或空格分隔列,用分号分隔行 dtype 输出矩阵的数据类型,可选项 copy 是否复制数据到一个新矩阵,可选项 应用举例: 12345678910>>> import numpy as np>>> a = np.matrix('1 2; 3 4')>>> amatrix([[1, 2], [3, 4]])>>> >>> b = np.matrix([[1, 2], [3, 4]])>>> bmatrix([[1, 2], [3, 4]]) 【1x04】mat() / asmatrix() / matrix() 的区别如果输入已经是一个矩阵或一个数组,则 mat() 和 asmatrix() 函数不会执行复制操作,相当于 matrix(data, copy=False) 对比举例: 123456789101112131415161718192021222324252627282930313233>>> import numpy as np>>> a = np.array([[1, 2], [3, 4]])>>> b = np.mat(a)>>> c = np.matrix(a)>>> d = np.asmatrix(a)>>> >>> aarray([[1, 2], [3, 4]])>>> bmatrix([[1, 2], [3, 4]])>>> cmatrix([[1, 2], [3, 4]])>>> dmatrix([[1, 2], [3, 4]])>>> >>> a[1][1] = 0>>> >>> aarray([[1, 2], [3, 0]])>>> bmatrix([[1, 2], [3, 0]])>>> c # matrix() 函数默认执行 copy 操作,所以数据不变matrix([[1, 2], [3, 4]])>>> dmatrix([[1, 2], [3, 0]]) 【1x05】numpy.bmat()numpy.bmat() 函数用于从字符串、嵌套序列或数组生成矩阵对象,一般用于创建复合矩阵。 基本语法:numpy.bmat(obj[, ldict=None, gdict=None]) 参数 描述 obj 数组或者字符串,如果 data 为字符串,则需要用逗号或空格分隔列,用分号分隔行 ldict 字典,用于替换当前帧中的本地操作数。如果 obj 不是字符串或 gdict 为 None,则将被忽略 gdict 字典,用于替换当前帧中的全局操作数。如果 obj 不是字符串则忽略 应用举例: 123456789101112131415161718192021>>> import numpy as np>>> a = np.mat('1 1; 1 1')>>> b = np.mat('2 2; 2 2')>>> c = np.mat('3 4; 5 6')>>> d = np.mat('7 8; 9 0')>>> >>> np.bmat([[a, b], [c, d]])matrix([[1, 1, 2, 2], [1, 1, 2, 2], [3, 4, 7, 8], [5, 6, 9, 0]])>>> np.bmat(np.r_[np.c_[a, b], np.c_[c, d]])matrix([[1, 1, 2, 2], [1, 1, 2, 2], [3, 4, 7, 8], [5, 6, 9, 0]])>>> np.bmat('a,b; c,d')matrix([[1, 1, 2, 2], [1, 1, 2, 2], [3, 4, 7, 8], [5, 6, 9, 0]]) 【1x06】numpy.matlib.empty()numpy.matlib.empty() 函数用于创建一个给定形状和数据类型的新矩阵。 基本语法:numpy.matlib.empty(shape[, dtype=None, order='C']) 参数 描述 shape 定义新矩阵的形状 dtype 数据类型,可选项 order 以行优先(C)或列优先(Fortran)的顺序存储多维数据在内存中,可选项 应用举例: 1234567>>> import numpy as np>>> print(np.matlib.empty((2, 2)))[[9.90263869e+067 8.01304531e+262] [2.60799828e-310 0.00000000e+000]]>>> print(np.matlib.empty((2, 2), dtype=int))[[ -793016358 -243407933] [ -959331519 -2060787213]] 【1x07】numpy.matlib.zeros()numpy.matlib.zeros() 函数创建一个以 0 填充的给定形状和类数据型的矩阵。 基本语法:numpy.matlib.zeros(shape[, dtype=None, order='C']) 参数 描述 shape 定义新矩阵的形状 dtype 数据类型,可选项 order 以行优先(C)或列优先(Fortran)的顺序存储多维数据在内存中,可选项 应用举例: 1234>>> import numpy as np>>> np.matlib.zeros((2, 3))matrix([[0., 0., 0.], [0., 0., 0.]]) 【1x08】numpy.matlib.ones()numpy.matlib.ones() 函数创建一个以 1 填充的给定形状和类数据型的矩阵。 基本语法:numpy.matlib.ones(shape[, dtype=None, order='C']) 参数 描述 shape 定义新矩阵的形状 dtype 数据类型,可选项 order 以行优先(C)或列优先(Fortran)的顺序存储多维数据在内存中,可选项 应用举例: 1234>>> import numpy as np>>> np.matlib.ones((2, 3))matrix([[1., 1., 1.], [1., 1., 1.]]) 【1x09】numpy.matlib.eye()numpy.matlib.eye() 函数创建一个对角线元素为 1,其他位置为零的矩阵。 基本语法:numpy.matlib.eye(n[, M=None, k=0, dtype=<class 'float'>, order='C']) 参数 描述 n 返回的矩阵的行数,int 类型 M 返回的矩阵的列数,int 类型,可选项,默认为 n k 对角线索引,可选项,0 表示主对角线,正值表示上对角线,负值表示下对角线,该对角线上元素的值将会是 1 dtype 返回矩阵的数据类型,可选项 order 以行优先(C)或列优先(Fortran)的顺序存储多维数据在内存中,可选项 应用举例: 12345678910111213>>> import numpy as np>>> print(np.matlib.eye(n=3, k=1))[[0. 1. 0.] [0. 0. 1.] [0. 0. 0.]]>>> print(np.matlib.eye(n=3, k=-1))[[0. 0. 0.] [1. 0. 0.] [0. 1. 0.]]>>> print(np.matlib.eye(n=3, M=4, k=0, dtype=int))[[1 0 0 0] [0 1 0 0] [0 0 1 0]] 【1x10】numpy.matlib.identity()numpy.matlib.identity() 函数创建一个给定大小的单位矩阵。 单位矩阵:在矩阵的乘法中,有一种矩阵起着特殊的作用,如同数的乘法中的1,这种矩阵被称为单位矩阵。它是个方阵,从左上角到右下角的对角线(称为主对角线)上的元素均为1。除此以外全都为0。 基本语法:numpy.matlib.identity(n[, dtype=None]) 参数 描述 n 返回的单位矩阵的大小,int 类型 dtype 可选项,返回的单位矩阵的数据类型 应用举例: 12345>>> import numpy as np>>> print(np.matlib.identity(3, dtype=int))[[1 0 0] [0 1 0] [0 0 1]] 【1x11】numpy.matlib.repmat()numpy.matlib.repmat() 函数用于重复数组或矩阵 m*n 次。 基本语法:numpy.matlib.repmat(a, m, n) 参数 描述 a 待处理的数组对象 m,n 沿第一轴和第二轴重复的次数 应用举例: 1234567891011121314>>> import numpy as np>>> a = np.array(1)>>> b = np.arange(4)>>> aarray(1)>>> barray([0, 1, 2, 3])>>> >>> print(np.matlib.repmat(a, 2, 3))[[1 1 1] [1 1 1]]>>> print(np.matlib.repmat(b, 2, 2))[[0 1 2 3 0 1 2 3] [0 1 2 3 0 1 2 3]] 【1x12】numpy.matlib.rand()numpy.matlib.rand() 函数创建一个给定大小的矩阵,其中的数据在 [0, 1) 区间随机取值来填充。 基本语法:numpy.matlib.rand(*args) 参数解释:*args:输出矩阵的形状,如果给定为 N 个整数,则每个整数指定一维的大小,如果以元组形式给出,则该元组表示输出矩阵完整的形状。 应用举例: 123456789101112>>> import numpy as np>>> print(np.matlib.rand(2, 3))[[0.27957871 0.48748368 0.0970184 ] [0.71062224 0.92503824 0.72415015]]>>>>>> print(np.matlib.rand((2, 3)))[[0.08814715 0.0307317 0.77775332] [0.81158748 0.09173265 0.77497881]]>>>>>> print(np.matlib.rand(2, 3), 4) # 如果第一个参数是元组,则其他参数将被忽略[[0.53407924 0.56006372 0.63903716] [0.56132381 0.90300814 0.44074964]] 4 【1x13】numpy.matlib.randn()numpy.matlib.randn() 函数创建一个标准正态分布的随机矩阵。 标准正态分布,是一个在数学、物理及工程等领域都非常重要的概率分布,在统计学的许多方面有着重大的影响力。期望值μ=0,即曲线图象对称轴为Y轴,标准差 σ=1 条件下的正态分布,记为 N(0,1)。 标准正态分布又称为 u 分布,是以 0 为均数、以 1 为标准差的正态分布,记为 N(0,1) 基本语法:numpy.matlib.randn(*args) 参数解释:*args:输出矩阵的形状,如果给定为 N 个整数,则每个整数指定一维的大小,如果以元组形式给出,则该元组表示输出矩阵完整的形状。 应用举例: 12345678>>> import numpy as np>>> print(np.matlib.randn(2, 3))[[ 0.82976978 -0.9798698 0.71262414] [ 2.31211127 -0.5090537 1.12357032]]>>> >>> print(2.5 * np.matlib.randn((2, 4)) + 3) # 2 x 4 矩阵 N(3, 6.25)[[-0.66974538 4.9354863 2.46138048 7.05576713] [ 0.80688217 1.79017491 3.78979646 -1.99071372]] 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105511641未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【2x00】NumPy 线性代数库线性代数是数学的一个分支,它的研究对象是向量,向量空间(或称线性空间),线性变换和有限维的线性方程组。NumPy 中也提供了线性代数函数库 numpy.linalg。 官方文档介绍:https://numpy.org/doc/1.18/reference/routines.linalg.html 【2x01】numpy.dot()numpy.dot() 函数用于计算两个数组的点积。 基本语法:numpy.dot(a, b[, out=None]) 参数 描述 a 第一个数组 b 第二个数组 out 可选项,放置结果的备用输出数组 如果 a 和 b 均为一维数组,计算的是这两个数组对应下标元素的乘积和(数学上称之为内积); 如果 a 和 b 均为二维数组,计算的是两个数组的矩阵乘积; 如果 a 和 b 均为多维数组,它的通用计算公式为:dot(a, b)[i,j,k,m] = sum(a[i,j,:] * b[k,:,m]),即结果数组中的每个元素都是数组 a 的最后一维上的所有元素与数组 b 的倒数第二维上的所有元素的乘积和。 应用举例: 12345678910111213>>> import numpy as np>>> a = np.array([[1,2],[3,4]])>>> b = np.array([[11,12],[13,14]])>>> print(np.dot(a,b)) # [[1*11+2*13, 1*12+2*14],[3*11+4*13, 3*12+4*14]][[37 40] [85 92]]>>> >>> c = np.arange(3*4*5*6).reshape((3,4,5,6))>>> d = np.arange(3*4*5*6)[::-1].reshape((5,4,6,3))>>> print(np.dot(c, d)[2,3,2,1,2,2])499128>>> print(sum(c[2,3,2,:] * d[1,2,:,2]))499128 【2x02】numpy.vdot()numpy.vdot() 函数返回两个向量的点积,如果第一个参数是复数,那么它的共轭复数会用于计算。 如果参数是多维数组,它会被展开。 共轭复数:两个实部相等,虚部互为相反数的复数互为共轭复数。 基本语法:numpy.vdot(a, b) 应用举例: 12345678910111213>>> import numpy as np>>> a = np.array([1+2j, 3+4j])>>> b = np.array([5+6j, 7+8j])>>> print(np.vdot(a, b)) # a 的共轭复数用于计算:(1-2j) * (5+6j) + (3-4j) * (7+8j)(70-8j)>>> print(np.vdot(b, a)) # b 的共轭复数用于计算:(1+2j) * (5-6j) + (3+4j) * (7-8j)(70+8j)>>> >>> >>> c = np.array([[1, 4], [5, 6]])>>> d = np.array([[4, 1], [2, 2]])>>> print(np.vdot(c, d)) # 1*4 + 4*1 + 5*2 + 6*230 【2x03】numpy.inner()numpy.inner() 函数计算一维数组的点积,对于其他维度,返回最后一个轴上的和的乘积。 基本语法:numpy.inner(a, b) 应用举例: 123456789101112>>> import numpy as np>>> a = np.array([[1,2],[3,4]])>>> b = np.array([[11,12],[13,14]])>>> print(np.inner(a,b)) # [[1*11+2*12, 1*13+2*14], [3*11+4*12, 3*13+4*14]][[35 41] [81 95]]>>> >>> >>> c = np.array([1,2,3])>>> d = np.array([0,1,0])>>> print(np.inner(c,d)) # 1*0+2*1+3*02 【2x04】numpy.outer()numpy.outer() 函数计算两个向量的外积。 基本语法:numpy.outer(a, b[, out=None]) 参数 描述 a 第一个向量,如果不是一维的则在计算前会将其展平 b 第一个向量,如果不是一维的则在计算前会将其展平 out 结果存储的位置,可选项,类似于 (M, N) 结构的 Ndarray 对象 外积一般指两个向量的向量积,若两向量:a = [a0, a1, ..., aM] b = [b0, b1, ..., bN],外积如下: 1234[[a0*b0 a0*b1 ... a0*bN ] [a1*b0 . [ ... . [aM*b0 aM*bN ]] 应用举例: 1234567891011121314>>> import numpy as np>>> a = np.array([1, 2, 3, 4])>>> b = np.array([5, 6, 7, 8])>>> print(np.outer(a, b))[[ 5 6 7 8] [10 12 14 16] [15 18 21 24] [20 24 28 32]]>>> >>> c = np.array(['a', 'b', 'c'], dtype=object)>>> print(np.outer(c, [1, 2, 3]))[['a' 'aa' 'aaa'] ['b' 'bb' 'bbb'] ['c' 'cc' 'ccc']] 【2x05】numpy.matmul()numpy.matmul() 函数计算两个矩阵的乘积。 矩阵的乘积运算: 设 A 为 m x p 的矩阵,B 为 p x n 的矩阵,那么称 m x n 的矩阵 C 为矩阵 A 与 B 的乘积,记作 C = AB,其中矩阵 C 中的第 i 行第 j 列元素可以表示为: $$ (AB){ij} = \\sum{k=1}^p a_{ik}b_{kj} = a_{i1}b_{1j} + a_{i2}b_{2j} + … + a_{ip}b_{pj} $$ $$A =\\left[\\begin{matrix}a_{1,1} & a_{1,2} & a_{1,3} \\a_{2,1} & a_{2,2} & a_{2,3}\\end{matrix}\\right]\\qquad\\qquad\\qquad\\qquad\\qquad\\qquad B =\\left[\\begin{matrix}b_{1,1} & b_{1,2} \\b_{2,1} & b_{2,2} \\b_{3,1} & b_{3,2}\\end{matrix}\\right]$$ $$C = AB =\\left[\\begin{matrix}a_{1,1}b_{1,1} & a_{1,2}b_{2,1} & a_{1,3}b_{3,1}, & a_{1,1}b_{1,2} & a_{1,2}b_{2,2} & a_{1,3}b_{3,2} \\a_{2,1}b_{1,1} & a_{2,2}b_{2,1} & a_{2,3}b_{3,1}, & a_{2,1}b_{1,2} & a_{2,2}b_{2,2} & a_{2,3}b_{3,2}\\end{matrix}\\right]$$ 矩阵相乘的条件: 当矩阵 A 的列数(column)等于矩阵 B 的行数(row)时,A 与 B 可以相乘; 矩阵 C 的行数等于矩阵 A 的行数,C 的列数等于 B 的列数; 乘积 C 的第 m 行第 n 列的元素等于矩阵 A 的第 m 行的元素与矩阵 B 的第 n 列对应元素乘积之和。 应用举例: 12345678910111213141516171819202122>>> import numpy as np>>> a = np.array([[1,0], [0,1]])>>> b = np.array([[4,1], [2,2]])>>> print(np.matmul(a, b))[[4 1] [2 2]]>>> >>> c = np.array([[1,0], [0,1]])>>> d = np.array([1,2])>>> print(np.matmul(c, d))[1 2]>>> print(np.matmul(d, c))[1 2]>>> >>> e = np.arange(8).reshape(2,2,2)>>> f = np.arange(4).reshape(2,2)>>> print(np.matmul(e, f))[[[ 2 3] [ 6 11]] [[10 19] [14 27]]] 【2x06】numpy.tensordot()numpy.tensordot() 函数计算两个不同维度矩阵的乘积。 基本语法:numpy.tensordot(a, b, axes=2) 参数 描述 a 第一个矩阵 b 第二个矩阵 axis 指定收缩的轴如果是一个整型 m,表示指定数组 a 的后 m 个轴和数组 b 的前 m 个轴分别进行内积,即对应位置元素相乘、再整体求和如果是一个列表 [m, n],那么表示 a 的第 m+1 个 (索引为m) 轴和 b 的第 n+1 (索引为n) 个轴进行内积 应用举例: 123456789101112131415161718192021222324252627282930>>> import numpy as np>>> a = np.random.randint(0, 9, (3, 4))>>> b = np.random.randint(0, 9, (4, 5))>>> aarray([[4, 0, 3, 6], [1, 3, 2, 2], [6, 1, 3, 4]])>>> barray([[1, 0, 0, 7, 6], [3, 8, 7, 5, 0], [4, 7, 0, 8, 0], [3, 8, 5, 0, 1]])>>> print(np.tensordot(a, b, 1))[[34 69 30 52 30] [24 54 31 38 8] [33 61 27 71 40]]>>>>>> c = np.array(range(1, 9)).reshape(2, 2, 2)>>> d = np.array(('a', 'b', 'c', 'd'), dtype=object).reshape(2, 2)>>> carray([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])>>> darray([['a', 'b'], ['c', 'd']], dtype=object)>>> print(np.tensordot(c, d))['abbcccdddd' 'aaaaabbbbbbcccccccdddddddd'] 【2x07】numpy.linalg.det()numpy.linalg.det() 函数计算矩阵的行列式。 阵行列式是指矩阵的全部元素构成的行列式,设 A=(aij) 是数域 P 上的一个 n 阶矩阵,则所有 A=(aij) 中的元素组成的行列式称为矩阵 A 的行列式,记为 |A| 或 det(A) 一个 2×2 矩阵的行列式可表示如下: $$ det = \\left[ \\begin{matrix} a & b \\ c & d \\end{matrix} \\right] = ad - bc $$ 应用举例: 1234>>> import numpy as np>>> a = np.array([[1, 2], [3, 4]])>>> print(np.linalg.det(a))-2.0000000000000004 【2x08】numpy.linalg.solve()numpy.linalg.solve() 函数求解线性矩阵方程或线性标量方程组。 $$\\left {\\begin{aligned}3x+y=9 \\x+2y=8\\end{aligned}\\right.\\qquad用矩阵可表示为:\\qquad\\left[\\begin{matrix}3 & 1 \\1 & 2\\end{matrix}\\right]\\left[\\begin{matrix}x & y\\end{matrix}\\right]= \\left[\\begin{matrix}9 & 8\\end{matrix}\\right]$$ 应用举例: 12345>>> import numpy as np>>> a = np.array([[3,1], [1,2]])>>> b = np.array([9,8])>>> print(np.linalg.solve(a, b))[2. 3.] 【2x09】numpy.linalg.inv()numpy.linalg.inv() 函数计算矩阵的逆矩阵。 设 A 是数域上的一个 n 阶矩阵,若在相同数域上存在另一个 n 阶矩阵 B,使得:AB = BA = E,则我们称 B 是 A 的逆矩阵,而 A 则被称为可逆矩阵。注:E 为单位矩阵。 应用举例: 12345678910>>> import numpy as np>>> a = np.array([[1, 2], [3, 4]])>>> b = np.linalg.inv(a)>>> print(b)[[-2. 1. ] [ 1.5 -0.5]]>>> >>> a * b == b * aarray([[ True, True], [ True, True]]) 【3x00】NumPy IO 操作NumPy IO 操作即读写磁盘上的文本数据或二进制数据,在 NumPy 中有专门的 .npy / npy 文件,.npy 文件用于储存单个 Ndarray 对象;.npz 文件用于储存多个 Ndarray 对象。 【3x01】numpy.save()numpy.save() 函数将数组保存到二进制文件(.npy 文件)中。 基本语法:numpy.save(file, arr[, allow_pickle=True, fix_imports=True]) 参数 描述 file 要保存的文件名,可以带路径,文件后缀为 .npy,若路径末尾没有后缀,则会默认加上 .npy 后缀 arr 要保存的数组 allow_pickle bool 值,可选项,是否允许使用 Python pickle 保存数组对象Python pickle 用于在保存到磁盘文件或从磁盘文件读取之前,对对象进行序列化和反序列化pickle 序列化后的数据,可读性差,人一般无法识别 fix_imports bool 值,可选项,强制以 Python 2 兼容方式对 Python 3 上的数组对象进行处理如果 fix_imports 为True,则 pickle 将尝试将新的 Python 3 名称映射到 Python 2 中使用的旧模块名称,以便 pickle 数据流可被 Python 2 读取 应用举例: 123>>> import numpy as np>>> a = np.array([[1,2,3], [4,5,6]])>>> np.save('D:\\\\file\\\\outfile.npy', a) 【3x02】numpy.load()numpy.load() 函数用于读取 .npy / npz 文件里面的内容。 基本语法:numpy.load(file[, mmap_mode=None, allow_pickle=False, fix_imports=True, encoding='ASCII']) 参数 描述 file 要读取的 npy 文件对象 mmap_mode 可选项,读取文件的模式,可选参数 r+ r w+ c,与 Python 读取文件模式类似,模式含义参见 numpy.memmap allow_pickle bool 值,可选项,是否允许使用 Python pickle 保存数组对象Python pickle 用于在保存到磁盘文件或从磁盘文件读取之前,对对象进行序列化和反序列化pickle 序列化后的数据,可读性差,人一般无法识别 fix_imports bool 值,可选项,强制以 Python 2 兼容方式对 Python 3 上的数组对象进行处理如果 fix_imports 为True,则 pickle 将尝试将新的 Python 3 名称映射到 Python 2 中使用的旧模块名称,以便 pickle 数据流可被 Python 2 读取 encoding str 类型,可选项,读取 Python2 字符串时使用什么编码仅当在 Python3 中加载 Python 2 生成的 pickled 文件(包括包含对象数组的 npy/npz 文件)时才有用不允许使用 latin1、ASCII 和 bytes 以外的值,因为它们可能损坏数字数据。默认值为 ASCII 应用举例: 123456>>> import numpy as np>>> a = np.array([[1,2,3], [4,5,6]])>>> np.save('D:\\\\file\\\\outfile.npy', a)>>> np.load('D:\\\\file\\\\outfile.npy')array([[1, 2, 3], [4, 5, 6]]) 【3x03】numpy.savez()numpy.savez() 函数将多个数组保存到二进制文件(.npz 文件)中。 基本语法:numpy.savez(file, *args[, **kwds]) 参数 描述 file 要保存的文件名,可以带路径,文件后缀为 .npz,若路径末尾没有后缀,则会默认加上 .npz 后缀 args 保存的数组,由于 Python 不知道外面 savez 的数组的名称,因此将使用 arr_0,arr_1 等名称保存数组,这些参数可以是任何表达式 kwds 关键字参数,可选项,数组将与关键字名称一起保存在文件中 应用举例: 12345678910111213141516171819>>> import numpy as np>>> a = np.array([[1,2,3],[4,5,6]])>>> b = np.arange(0, 1.0, 0.1)>>> c = np.sin(b) # c 使用关键字参数 sin_array>>> np.savez('D:\\\\file\\\\outfile.npz', a, b, sin_array=c)>>> r = np.load('D:\\\\file\\\\outfile.npz')>>> print(r.files) # 查看各个数组名称['sin_array', 'arr_0', 'arr_1']>>> >>> print(r['arr_0']) # 数组 a[[1 2 3] [4 5 6]]>>> >>> print(r['arr_1']) # 数组 b[0. 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9]>>> >>> print(r['sin_array']) # 数组 c[0. 0.09983342 0.19866933 0.29552021 0.38941834 0.47942554 0.56464247 0.64421769 0.71735609 0.78332691] 【3x04】numpy.savetxt()numpy.savetxt() 函数将数组保存到文本文件中(txt)。 基本语法:numpy.savetxt(fname, X[, fmt='%.18e', delimiter=' ', newline='n', header='', footer='', comments='# ', encoding=None]) 参数 描述 fname 要保存的文件名,可以带路径,如果文件后缀为 .gz,则文件将自动以压缩格式 gzip 保存 X 要保存的数组 fmt 格式序列或多格式字符串,可选项 delimiter 指定各种分隔符、针对特定列的转换器函数、需要跳过的行数等,可选项 newline 字符串或字符分隔线,可选项 header 写入文件开头的字符串,可选项 footer 写入文件末尾的字符串,可选项 comments 注释,在 header 和 footer 字符串之前添加的字符串,可选项 encoding 对输出文件进行编码,可选项 应用举例: 12345678910111213>>> import numpy as np>>> a = np.array([1,2,3,4,5])>>> np.savetxt('D:\\\\file\\\\outfile.txt', a)>>> np.loadtxt('D:\\\\file\\\\outfile.txt')array([1., 2., 3., 4., 5.])>>> >>> b = np.arange(0,10,0.5).reshape(4,-1)>>> np.savetxt('D:\\\\file\\\\outfile2.txt', b, fmt=\"%d\", delimiter=',') # 保存为整数,以逗号分隔>>> np.loadtxt('D:\\\\file\\\\outfile2.txt', delimiter=',') # 读取数据时也要指定相同的分隔符array([[0., 0., 1., 1., 2.], [2., 3., 3., 4., 4.], [5., 5., 6., 6., 7.], [7., 8., 8., 9., 9.]]) 【3x05】numpy.loadtxt()numpy.loadtxt() 函数用于读取文本文件(txt)里面的内容。 基本语法:numpy.loadtxt(fname[, dtype=<class 'float'>, comments='#', delimiter=None, converters=None, skiprows=0, usecols=None, unpack=False, ndmin=0, encoding='bytes', max_rows=None]) 重要参数解释: 参数 描述 fname 要读取的文件,文件名或生成器。如果文件扩展名是 .gz 或 .bz2,则首先将文件解压缩,注意,生成器应返回字节字符串 dtype 可选项,结果数组的数据类型 comments str 或 str 序列,可选项,用于指示注释开始的字符或字符列表 delimiter str 类型,可选项,指定分隔符 skiprows int 类型,可选项,跳过前 n 行,一般用于跳过第一行表头 usecols int 类型的索引值,读取指定的列 unpack bool 值,可选项,如果为True,则会对返回的数组进行转置 ndmin int 类型,可选项,返回的数组将至少具有 ndmin 维度,否则一维轴将被压缩 encoding str 类型,可选项,用于解码输入文件的编码 max_rows int 类型,可选项,读取 skiprows 行之后的最大行内容。默认值是读取所有行 应用举例: 12345>>> import numpy as np>>> a = np.array([1,2,3,4,5])>>> np.savetxt('D:\\\\file\\\\outfile.txt', a)>>> np.loadtxt('D:\\\\file\\\\outfile.txt')array([1., 2., 3., 4., 5.]) 【3x06】numpy.genfromtxt()numpy.genfromtxt() 函数同样用于读取文本文件(txt)里面的内容。该函数比 loadtxt() 函数功能更加强大,genfromtxt() 主要面向结构数组和缺失数据处理。 官方文档介绍:https://numpy.org/doc/1.18/reference/generated/numpy.genfromtxt.html 推荐文章:https://www.cnblogs.com/Simplelee/p/8975763.html 主要语法:numpy.genfromtxt(fname[, dtype=<class 'float'>, comments='#', delimiter=None, skip_header=0, skip_footer=0, converters=None, missing_values=None, filling_values=None, usecols=None, encoding='bytes']) 主要参数解释: 参数 描述 fname 要读取的文件,文件名或生成器。如果文件扩展名是 .gz 或 .bz2,则首先将文件解压缩,注意,生成器应返回字节字符串 dtype 可选项,结果数组的数据类型 comments str 或 str 序列,可选项,用于指示注释开始的字符或字符列表 delimiter str 类型,可选项,指定分隔符 skip_header int 类型,可选项,文件开头要跳过的行数 skip_footer int 类型,可选项,文件末尾要跳过的行数 converters 变量,可选项,将列的数据转换为值的一组函数还可以为丢失的数据提供默认值:converters = {3: lambda s: float(s or 0)} missing_values 变量,可选项,与缺少的数据相对应的字符串集,默认情况下使用空格表示缺失 filling_values 变量,可选项,缺少数据时用作默认值的一组值 usecols 序列,可选项,读取指定的列 encoding str 类型,可选项,用于解码输入文件的编码 应用举例: 12345678910111213>>> import numpy as np>>> a = np.array([1,2,3,4,5])>>> np.savetxt('D:\\\\file\\\\outfile.txt', a)>>> np.genfromtxt('D:\\\\file\\\\outfile.txt')array([1., 2., 3., 4., 5.])>>>>>> b = np.arange(0,10,0.5).reshape(4,-1)>>> np.savetxt('D:\\\\file\\\\outfile2.txt', b, fmt=\"%d\", delimiter=\",\")>>> np.genfromtxt('D:\\\\file\\\\outfile2.txt', delimiter=',')array([[0., 0., 1., 1., 2.], [2., 3., 3., 4., 4.], [5., 5., 6., 6., 7.], [7., 8., 8., 9., 9.]]) 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105511641未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"NumPy","slug":"Python-数据分析/NumPy","permalink":"https://www.itrhx.com/categories/Python-数据分析/NumPy/"}],"tags":[{"name":"NumPy","slug":"NumPy","permalink":"https://www.itrhx.com/tags/NumPy/"},{"name":"矩阵","slug":"矩阵","permalink":"https://www.itrhx.com/tags/矩阵/"},{"name":"线性代数","slug":"线性代数","permalink":"https://www.itrhx.com/tags/线性代数/"},{"name":"IO操作","slug":"IO操作","permalink":"https://www.itrhx.com/tags/IO操作/"}]},{"title":"Python 数据分析三剑客之 NumPy(五):数学 / 算术 / 统计 / 排序 / 条件 / 判断函数合集","slug":"A66-NumPy-05","date":"2020-03-28T12:23:03.859Z","updated":"2020-07-06T13:28:12.800Z","comments":true,"path":"2020/03/28/A66-NumPy-05/","link":"","permalink":"https://www.itrhx.com/2020/03/28/A66-NumPy-05/","excerpt":"","text":"NumPy 系列文章: Python 数据分析三剑客之 NumPy(一):理解 NumPy / 数组基础 Python 数据分析三剑客之 NumPy(二):数组索引 / 切片 / 广播 / 拼接 / 分割 Python 数据分析三剑客之 NumPy(三):数组的迭代与位运算 Python 数据分析三剑客之 NumPy(四):字符串函数总结与对比 Python 数据分析三剑客之 NumPy(五):数学 / 算术 / 统计 / 排序 / 条件 / 判断函数合集 Python 数据分析三剑客之 NumPy(六):矩阵 / 线性代数库与 IO 操作 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105398131未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【1x00】NumPy 函数速查表 NumPy 三角函数 函数 描述 sin() 正弦函数 cos() 余弦函数 tan() 正切函数 arcsin() 反正弦函数 arccos() 反余弦函数 arctan() 反正切函数 NumPy 舍入函数 函数 描述 around() 将指定数字四舍五入到指定的小数位 rint() 将指定数字四舍五入到最近的整数 floor() 返回小于或者等于指定表达式的最大整数,即向下取整 ceil() 返回大于或者等于指定表达式的最小整数,即向上取整 NumPy 算术函数 函数 描述 add() 数组元素加法运算 subtract() 数组元素减法运算 multiply() 数组元素乘法运算 divide() 数组元素除法运算 reciprocal() 返回数组元素的倒数 power() 返回数组元素的乘幂 mod() 返回数组元素的相除后的余数 remainder() 返回数组元素的相除后的余数 NumPy 统计函数 函数 描述 amax() 计算数组元素沿指定轴的最大值 amin() 计算数组元素沿指定轴的最小值 argmax() 计算数组元素沿指定轴的最大值的索引值 argmin() 计算数组元素沿指定轴的最小值的索引值 sum() 计算数组中所有元素的和 cumsum() 返回一个一维数组,每个元素都是之前所有元素的累加和 cumprod() 返回一个一维数组,每个元素都是之前所有元素的累乘积 ptp() 计算数组元素最大值与最小值的差 percentile() 计算多维数组的任意百分位数 median() 计算数组元素的中位数 mean() 计算数组元素的算术平均值 average() 计算数组元素的加权平均值 std() 计算数组元素的标准差 var() 计算数组元素的方差 NumPy 排序函数 sort() 将原数组元素按照从小到大排序 msort() 将原数组元素按照第一个轴的从小到大排序 argsort() 将元素从小到大排列,提取对应的索引值并返回 lexsort() 将多个序列按照从小到大排序,返回其索引值 sort_complex() 对复数数组进行从小到大排序 partition() 对数组进行分区排序 argpartition() 对数组进行分区排序,返回元素的索引值 unique() 将数组元素去重后返回从小到大的排序结果 NumPy 条件函数 nonzero() 返回原数组中非零元素的索引值 where() 返回数组中满足指定条件的元素的索引值 extract() 返回数组中满足指定条件的元素 NumPy 判断函数 any() 至少有一个元素满足指定条件,则返回 True,否则返回 False all() 所有的元素满足指定条件,则返回 True,否则返回 False 【2x00】NumPy 数学函数NumPy 数学函数包含三角函数、舍入函数等。 【2x01】sin() / cos() / tan()numpy.sin()、numpy.cos()、numpy.tan() 分别对应正弦函数、余弦函数、正切函数。 在求三角函数时,会先将角度转化成弧度,在 NumPy 中的转化公式:角度 * numpy.pi/180 应用举例: 12345678>>> import numpy as np>>> a = np.array([0, 30, 45, 60 ,90])>>> print(np.sin(a*np.pi/180))[0. 0.5 0.70710678 0.8660254 1. ]>>> print(np.cos(a*np.pi/180))[1.00000000e+00 8.66025404e-01 7.07106781e-01 5.00000000e-01 6.12323400e-17]>>> print(np.tan(a*np.pi/180))[0.00000000e+00 5.77350269e-01 1.00000000e+00 1.73205081e+00 1.63312394e+16] 【2x02】arcsin() / arccos() / arctan()numpy.arcsin()、numpy.arccos()、numpy.arctan() 分别对应反正弦函数、反余弦函数、反正切函数。 在求三角函数时,会先将角度转化成弧度,在 NumPy 中的转化公式:角度 * numpy.pi/180 arcsin、arccos、arctan 接收的参数是三角函数值,函数返回给定角度的 sin,cos 和 tan 的反三角函数,如果 sinθ=x,那么 arcsinx=θ,其他类似,这些函数的结果可以通过 numpy.degrees() 函数将弧度转换为角度。 12345678910>>> import numpy as np>>> a = np.array([0, 30, 45, 60 ,90])>>> a_sin = np.sin(a*np.pi/180)>>> a_arcsin = np.arcsin(a_sin)>>> print(a_sin) # 角度的正弦值[0. 0.5 0.70710678 0.8660254 1. ]>>> print(a_arcsin) # 角度的反正弦值,返回值的单位为弧度[0. 0.52359878 0.78539816 1.04719755 1.57079633]>>> print(np.degrees(a_arcsin)) # 弧度转化为角度[ 0. 30. 45. 60. 90.] 【2x03】around() / rint() / floor() / ceil()1、numpy.around() 函数将指定数字四舍五入到指定的小数位,可指定保留的小数位数。 基本语法:numpy.around(a[, decimals=0, out=None]) 参数 描述 a 输入数组 decimals int 类型,可选项,舍入的小数位数,默认值为 0,如果为负,整数将四舍五入到小数点左侧的位置 out ndarray 对象,可选项,放置结果的备用输出数组。它必须具有与预期输出相同的形状,但是如有必要,将强制转换输出值的类型 应用举例: 12345678>>> import numpy as np>>> a = np.array([13, 1.4, 6.23, 12.834])>>> print(np.around(a))[13. 1. 6. 13.]>>> print(np.around(a, decimals=1))[13. 1.4 6.2 12.8]>>> print(np.around(a, decimals=-1))[10. 0. 10. 10.] 2、numpy.rint() 函数将指定数字四舍五入到最近的整数,不保留小数位。 应用举例: 123>>> import numpy as np>>> print(np.rint([-1.7, -1.5, -0.2, 0.2, 1.5, 1.7, 2.0]))[-2. -2. -0. 0. 2. 2. 2.] 3、numpy.floor() 函数会返回小于或者等于指定表达式的最大整数,即向下取整。 应用举例: 123>>> import numpy as np>>> print(np.floor([-1.7, -1.5, -0.2, 0.2, 1.5, 1.7, 2.0]))[-2. -2. -1. 0. 1. 1. 2.] 4、numpy.ceil() 函数会返回大于或者等于指定表达式的最小整数,即向上取整。 应用举例: 123>>> import numpy as np>>> print(np.ceil([-1.7, -1.5, -0.2, 0.2, 1.5, 1.7, 2.0]))[-1. -1. -0. 1. 2. 2. 2.] 【3x00】NumPy 算术函数NumPy 算术函数包含了基本的加减乘除运算、求倒数、幂、余数等。 【3x01】add() / subtract() / multiply() / divide()add()、subtract()、multiply()、divide() 分别对应 NumPy 中的加减乘除运算。 注意:两个数组必须具有相同的形状或符合数组的广播规则才能进行运算。 应用举例: 12345678910111213141516171819>>> import numpy as np>>> a = np.array([[0, 1, 2], [3, 4, 5], [6, 7, 8]])>>> b = np.array([5, 5, 5])>>> print(np.add(a, b))[[ 5 6 7] [ 8 9 10] [11 12 13]]>>> print(np.subtract(a, b))[[-5 -4 -3] [-2 -1 0] [ 1 2 3]]>>> print(np.multiply(a, b))[[ 0 5 10] [15 20 25] [30 35 40]]>>> print(np.divide(a, b))[[0. 0.2 0.4] [0.6 0.8 1. ] [1.2 1.4 1.6]] 【3x02】reciprocal() / power() / mod() / remainder()1、numpy.reciprocal() 函数用于返回各数组元素的倒数。 应用举例: 123>>> import numpy as np>>> print(np.reciprocal([0.25, 4, 1, 2.10]))[4. 0.25 1. 0.47619048] 2、numpy.power() 函数将第一个数组中的元素作为底数,计算它与第二个数组中相应元素的幂。 应用举例: 12345678>>> import numpy as np>>> a = np.array([5, 10, 100])>>> b = np.array([3, 2, 1])>>> print(np.power(a, b))[125 100 100]>>>>>> print(np.power(a, 2))[ 25 100 10000] 3、numpy.mod() 与 numpy.remainder() 都可以计算数组中相应元素相除后的余数。 应用举例: 1234567891011>>> import numpy as np>>> a = np.array([10, 15, 20])>>> b = np.array([3, 4, 5])>>> print(np.mod(a, b))[1 3 0]>>> print(np.remainder(a, b))[1 3 0]>>> print(np.mod(a, 6))[4 3 2]>>> print(np.remainder(a, 9))[1 6 2] 【3x03】absolute() / isnan()1、numpy.absolute() 函数用于计算元素的绝对值。 应用举例: 123>>> import numpy as np>>> print(np.absolute([-1.2, 1.2, 13, -10]))[ 1.2 1.2 13. 10. ] 2、numpy.isnan() 函数用于判断元素是否为 NaN(Not a Number)。 应用举例: 1234567>>> import numpy as np>>> np.isnan(np.nan)True>>> np.isnan(np.inf)False>>> print(np.isnan([np.nan, 2, 3, np.nan]))[ True False False True] 【4x00】NumPy 统计函数NumPy 统计函数包含了计算最大值、最小值、最大值与最小值的差、百分位数、中位数、算术平均值、加权平均值、标准差与方差等。 【4x01】amax() / amin()numpy.amax() 和 numpy.amin() 函数分别用于计算数组中的元素沿指定轴的最大值和最小值。 基本语法: numpy.amax(a[, axis=None, out=None, keepdims=<no value>, initial=<no value>, where=<no value>]) numpy.amin(a[, axis=None, out=None, keepdims=<no value>, initial=<no value>, where=<no value>]) 参数解释: 参数 描述 a 待处理的数组对象 axis 指定轴,可选项,整数或者整数元组类型 out ndarray 对象,可选项,放置结果的备用输出数组,必须具有与预期输出数组相同的形状和缓冲区长度 keepdims bool 类型,可选项,是否保持数组的二维特性 initial 初始值标量,可选项,如果设置了标量,则除了元素之间的比较外,还会和标量进行对比 where 比较条件,通常和 initial 参数一起使用如果当前是 amax 函数,where 为 True 时则会比较最大值, Flase 则会比较最小值该参数含义比较模糊,参考资料较少,准确描述请参考官方文档 应用举例: 1234567891011121314151617181920>>> import numpy as np>>> a = np.array([[2, 4], [8, 9]])>>> print(a)[[2 4] [8 9]]>>> >>> print(np.amax(a))9>>> >>> print(np.amax(a, axis=0)) # 元素按行比较[8 9]>>>>>> print(np.amax(a, axis=0, keepdims=True)) # 元素按行比较并保持数组的二维特性[[8 9]]>>> >>> print(np.amax(a, axis=1)) # 元素按列比较[4 9]>>> >>> print(np.amax(a, axis=1, initial=5)) # 元素按列比较(包括标量一起比较)[5 9] 【4x02】argmax() / argmin()numpy.argmax() 和 numpy.argmin() 函数分别沿指定轴返回最大元素和最小元素的索引值。 基本语法:numpy.argmax(a[, axis=None, out=None]);numpy.argmin(a[, axis=None, out=None]) 参数 描述 a 待处理的数组对象 axis 指定轴,可选项,整数或者整数元组类型,若未指定,则在操作前会将数组展开 out 可选项,放置结果的备用输出数组,必须具有与预期输出数组相同的形状和缓冲区长度 应用举例: 123456789101112131415161718>>> import numpy as np>>> a = np.array([[30,40,70],[80,20,10],[50,90,60]])>>> print(a)[[30 40 70] [80 20 10] [50 90 60]]>>> >>> print (np.argmax(a))7>>> >>> print(np.argmin(a))5>>> >>> print(np.argmax(a, axis=0))[1 2 0]>>> >>> print(np.argmin(a, axis=0))[0 1 1] 【4x03】sum()numpy.sum() 函数用于计算所有元素的和。 基本语法:numpy.sum(a, axis=None, dtype=None, out=None, keepdims=<no value>, initial=<no value>, where=<no value>) 参数 描述 a 待处理的数组对象 axis 指定轴,可选项,整数或者整数元组类型,若未指定则运算前会将数组展平 dtype 指定数据类型,可选项 out 可选项,放置结果的备用输出数组,必须具有与预期输出数组相同的形状和缓冲区长度 keepdims bool 类型,可选项,是否保持数组的二维特性 initial 初始值标量,可选项,如果设置了标量,则除了元素之间的求和外,还会和标量进行求和 where 求和条件,总和中要包括的元素 应用举例: 1234567891011>>> import numpy as np>>> print(np.sum([[0, 1], [0, 5]]))6>>> print(np.sum([[0, 1], [0, 5]], axis=0))[0 6]>>> print(np.sum([[0, 1], [0, 5]], axis=1))[1 5]>>> print(np.sum([[0, 1], [np.nan, 5]], where=[False, True], axis=1))[1. 5.]>>> print(np.sum([10], initial=5))15 【4x04】cumsum() / cumprod()numpy.cumsum():返回一个一维数组,每个元素都是之前所有元素的累加和。numpy.cumprod():返回一个一维数组,每个元素都是之前所有元素的累乘积。 基本语法:numpy.cumsum(a, axis=None, dtype=None, out=None);numpy.cumprod(a, axis=None, dtype=None, out=None) 参数 描述 a 待处理的数组对象 axis 指定轴,可选项,整数或者整数元组类型,若未指定则运算前会将数组展平 dtype 指定数据类型,可选项 out 可选项,放置结果的备用输出数组,必须具有与预期输出数组相同的形状和缓冲区长度 应用举例: 1234567891011121314151617181920212223>>> import numpy as np>>> a = np.array([[1,2,3], [4,5,6]])>>> print(np.cumsum(a))[ 1 3 6 10 15 21]>>> >>> print(np.cumsum(a, axis=0))[[1 2 3] [5 7 9]]>>> >>> print(np.cumsum(a, axis=1))[[ 1 3 6] [ 4 9 15]]>>> >>> print(np.cumprod(a))[ 1 2 6 24 120 720]>>> >>> print(np.cumprod(a, axis=0))[[ 1 2 3] [ 4 10 18]]>>> >>> print(np.cumprod(a, axis=1))[[ 1 2 6] [ 4 20 120]] 【4x05】ptp()numpy.ptp() 函数用于计算数组中元素最大值与最小值的差。 基本语法:numpy.ptp(a[, axis=None, out=None, keepdims=<no value>]) 参数 描述 a 待处理的数组对象 axis 指定轴,可选项,整数或者整数元组类型 out 可选项,放置结果的备用输出数组,必须具有与预期输出数组相同的形状和缓冲区长度 keepdims bool 类型,可选项,是否保持数组的二维特性 应用举例: 123456789101112131415161718>>> import numpy as np>>> a = np.array([[2,5,8],[7,6,3],[2,9,4]])>>> print(a)[[2 5 8] [7 6 3] [2 9 4]]>>> print(np.ptp(a))7>>> >>> print(np.ptp(a, axis=0))[5 4 5]>>> >>> print(np.ptp(a, axis=1))[6 4 7]>>> print(np.ptp(a, axis=1, keepdims=True))[[6] [4] [7]] 【4x06】percentile()numpy.percentile() 函数用于计算一个多维数组的任意百分位数。 百分位数:统计学术语,如果将一组数据从小到大排序,并计算相应的累计百分位,则某一百分位所对应数据的值就称为这一百分位的百分位数。可表示为:一组 n 个观测值按数值大小排列。如:处于 p% 位置的值称第 p 百分位数。 基本语法:numpy.percentile(a, q[, axis=None, out=None, overwrite_input=False, interpolation='linear', keepdims=False]) 参数 描述 a 待处理的数组对象 q 要计算的百分位数,在 [0, 100] 之间 axis 指定轴,可选项,整数或者整数元组类型 out 可选项,放置结果的备用输出数组,必须具有与预期输出数组相同的形状和缓冲区长度 overwrite_input bool 值,可选项,如果为True,则允许通过中间计算来修改输入数组 a 以节省内存在这种情况下,此操作完成后 a 的内容是不确定的 interpolation 可选项,指定当所需百分比位于两个数据点 i<j 之间时要使用的插值方法:linear:i + (j - i) * fraction,其中 fraction 是由 i 和 j 包围的索引的分数部分lower:i;higher:j;nearest:i 或 j,以最近者为准;midpoint:(i + j) / 2 keepdims bool 类型,可选项,是否保持数组的二维特性 应用举例: 123456789101112131415161718>>> import numpy as np>>> a = np.array([[10, 7, 4], [3, 2, 1]])>>> print(a)[[10 7 4] [ 3 2 1]]>>> >>> print(np.percentile(a, 50))3.5>>> >>> print(np.percentile(a, 50, axis=0))[6.5 4.5 2.5]>>> >>> print(np.percentile(a, 50, axis=1))[7. 2.]>>> >>> print(np.percentile(a, 50, axis=1, keepdims=True))[[7.] [2.]] 【4x07】median()numpy.median() 函数用于计算数组元素的中位数。 基本语法:numpy.median(a[, axis=None, out=None, overwrite_input=False, keepdims=False]) 参数 描述 a 待处理的数组对象 axis 指定轴,可选项,整数或者整数元组类型 out 可选项,放置结果的备用输出数组,必须具有与预期输出数组相同的形状和缓冲区长度 overwrite_input bool 值,可选项,如果为True,则允许通过中间计算来修改输入数组 a 以节省内存在这种情况下,此操作完成后 a 的内容是不确定的 keepdims bool 类型,可选项,是否保持数组的二维特性 应用举例: 1234567891011121314>>> import numpy as np>>> a = np.array([[10, 7, 4], [3, 2, 1]])>>> print(a)[[10 7 4] [ 3 2 1]]>>>>>> print(np.median(a))3.5>>>>>> print(np.median(a, axis=0))[6.5 4.5 2.5]>>> >>> print(np.median(a, axis=1))[7. 2.] 【4x08】mean()numpy.mean() 函数计算数组中元素的算术平均值。 算术平均值:沿轴的元素的总和除以元素的数量。 基本语法:numpy.mean(a[, axis=None, dtype=None, out=None]) 参数 描述 a 待处理的数组对象 axis 指定轴,可选项,整数或者整数元组类型 dtype 可选项,用于计算平均值的类型 out 可选项,放置结果的备用输出数组,必须具有与预期输出数组相同的形状和缓冲区长度 应用举例: 1234567891011121314>>> import numpy as np>>> a = np.array([[10, 7, 4], [3, 2, 1]])>>> print(a)[[10 7 4] [ 3 2 1]]>>> >>> print(np.mean(a))4.5>>> >>> print(np.mean(a, axis=0))[6.5 4.5 2.5]>>> >>> print(np.mean(a, axis=1))[7. 2.] 【4x09】average()numpy.average() 函数根据在另一个数组中给出的各自的权重来计算数组中元素的加权平均值。 加权平均值:将各数值乘以相应的权数,然后加总求和得到总体值,再除以总的单位数。 例如:现有数组 [1, 2, 3, 4],相应的权重为 [5, 6, 7, 8],则计算方法为:(1*5+2*6+3*7+4*8)/(5+6+7+8)≈ 2.6923 基本语法:numpy.average(a[, axis=None, weights=None, returned=False]) 参数 描述 a 待处理的数组对象 axis 指定轴,可选项,整数或者整数元组类型 weights 可选项,与 a 中的值相关联的权重数组,如果 weights=None,则 a 中的所有数据都具有 1 的权重,相当于 mean 函数 returned 可选项,bool 类型,默认为 False,如果为 True,则返回元组 (加权平均值, 权重),否则只返回加权平均值 应用举例: 12345678910>>> import numpy as np>>> a = np.array([1, 2, 3, 4])>>> print(np.average(a)) # 不指定权重时相当于 mean 函数2.5>>> >>> print(np.average(a, weights=[5, 6, 7, 8]))2.6923076923076925>>> >>> print(np.average(a, weights=[5, 6, 7, 8], returned=True))(2.6923076923076925, 26.0) 【4x10】std() / var()numpy.std() 和 numpy.var() 分别用于计算数组元素的标准差与方差。 标准差是一组数据平均值分散程度的一种度量,标准差是方差的算术平方根。 方差为 S2,标准差为 S,计算公式如下: $$S^2 = \\frac {1} {n} \\sum_{i=1} ^ {n} (x_i - \\overline {x})^2$$ $$S = \\sqrt {S^2}$$ 基本语法: numpy.std(a, axis=None, dtype=None, out=None, ddof=0, keepdims=<no value>) numpy.var(a[, axis=None, dtype=None, out=None, ddof=0, keepdims=<no value>]) 参数 描述 a 待处理的数组对象 axis 指定轴,可选项,整数或者整数元组类型 dtype 可选项,值的数据类型 out 可选项,放置结果的备用输出数组,必须具有与预期输出数组相同的形状和缓冲区长度 ddof 自由度(Delta Degrees of Freedom),计算中使用的除数是 N-ddof,其中 N 表示元素的数量,ddof 默认为零 keepdims bool 类型,可选项,是否保持数组的二维特性 应用举例: 1234567891011121314151617181920212223>>> import numpy as np>>> a = np.array([[1, 2], [3, 4]])>>> print(a)[[1 2] [3 4]]>>> >>> print(np.std(a))1.118033988749895>>> >>> print(np.std(a, axis=0))[1. 1.]>>> >>> print(np.std(a, axis=1))[0.5 0.5]>>> >>> print(np.var(a))1.25>>> >>> print(np.var(a, axis=0))[1. 1.]>>> >>> print(np.var(a, axis=1))[0.25 0.25] 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105398131未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【5x00】NumPy 排序函数NumPy 排序函数中可以指定使用的排序方法,各种分类算法的特征在于它们的平均速度,最坏情况下的性能,工作空间大小以及它们是否稳定,三种算法对比如下: 方法 速度 最坏情况 工作空间 稳定性 快速排序(quicksort) 1 O(n^2) 0 no 归并排序(mergesort) 2 O(n*log(n)) ~n/2 yes 堆排序(heapsort) 3 O(n*log(n)) 0 no 【5x01】sort()numpy.sort() 函数会将原数组元素从小到大排序,返回输入数组的排序副本。 基本语法:numpy.sort(a[, axis=-1, kind='quicksort', order=None]) 参数 描述 a 要排序的数组 axis 要排序的轴,可选项,如果为 None,则在排序之前会将数组展平,默认值为 -1,它沿着最后一个轴排序 kind 排序算法,可选项,默认值为快速排序(quicksort) order 字符串或者字符串列表,可选项,若指定 order 值,将按照该字段进行排序 应用举例: 12345678910111213141516>>> import numpy as np>>> a = np.array([[1,4],[3,1]])>>> print(np.sort(a))[[1 4] [1 3]]>>> >>> print(np.sort(a, axis=None)) # 数组将展平[1 1 3 4]>>> >>> print(np.sort(a, axis=0))[[1 1] [3 4]]>>> >>> print(np.sort(a, axis=1))[[1 4] [1 3]] 123456789101112>>> import numpy as np>>> dtype = [('name', 'S10'), ('height', float), ('age', int)]>>> values = [('Arthur', 1.8, 41), ('Lancelot', 1.9, 38), ('Galahad', 1.7, 38)]>>> a = np.array(values, dtype=dtype)>>> print(a)[(b'Arthur', 1.8, 41) (b'Lancelot', 1.9, 38) (b'Galahad', 1.7, 38)]>>> >>> print(np.sort(a, order='height')) # 按 height 排序[(b'Galahad', 1.7, 38) (b'Arthur', 1.8, 41) (b'Lancelot', 1.9, 38)]>>> >>> print(np.sort(a, order=['age', 'height'])) # 按 age 排序,如果 age 相等,则按 height 排序[(b'Galahad', 1.7, 38) (b'Lancelot', 1.9, 38) (b'Arthur', 1.8, 41)] 【5x02】msort()numpy.msort() 函数将数组按第一个轴从小到大排序,返回排序后的数组副本,相当于 numpy.sort(a, axis=0) 应用举例: 1234567>>> import numpy as np>>> print(np.msort([[1, 4, 5], [2, 1, 3]]))[[1 1 3] [2 4 5]]>>> print(np.sort([[1, 4, 5], [2, 1, 3]], axis=0))[[1 1 3] [2 4 5]] 【5x03】argsort()numpy.argsort() 函数将原数组元素从小到大排列,提取其对应在原数组中的索引值并返回。 举例:原数组为:[3, 1, 2],从小到大排列:[1, 2, 3],排列后的各元素在原数组中对应的索引值:[1, 2, 0] 基本语法:numpy.argsort(a[, axis=-1, kind=None, order=None]) 参数 描述 a 要排序的数组 axis 要排序的轴,可选项,如果为 None,则在排序之前会将数组展平,默认值为 -1,它沿着最后一个轴排序 kind 排序算法,可选项,默认值为快速排序(quicksort) order 字符串或者字符串列表,可选项,若指定 order 值,将按照该字段进行排序 应用举例: 1234567891011121314151617>>> import numpy as np>>> a = np.array([3, 1, 2])>>> print(np.argsort(a))[1 2 0]>>> >>> b = np.array([[0, 3], [2, 2]])>>> print(np.argsort(b))[[0 1] [0 1]]>>> >>> print(np.argsort(b, axis=0))[[0 1] [1 0]]>>> >>> print(np.argsort(b, axis=1))[[0 1] [0 1]] 【5x04】lexsort()numpy.lexsort() 函数使用键序列执行间接稳定排序,用于对多个序列进行排序,返回其索引值。 基本语法:numpy.lexsort(keys, axis=-1) 参数 描述 keys 类似于 (k, N) 的要排序的数组 axis 指定要排序的轴,默认对最后一个轴进行排序 举例:现有数组 a = [1,5,1,4,3,4,4],b = [9,4,0,4,0,2,1],利用 lexsort() 函数排序后的结果为:[2,0,4,6,5,3,1],排序过程如下: 假设数组 a1 为语文成绩:a1 = [1,5,1,4,3,4,4]假设数组 b1 为数学成绩:b1 = [9,4,0,4,0,2,1]数组索引值 c1 为同学姓名: c1 = [0,1,2,3,4,5,6] 1、首先按照语文成绩进行排名:语文成绩(数组 a)从小到大排名:a2 = [1,1,3,4,4,4,5],对应的学生姓名(索引值)为:c2 = [0,2,4,3,5,6,1],现在可以看到:0、2 同学的语文成绩都一样,均为 13、5、6 同学的语文成绩都一样,均为 4 2、接下来,对于语文成绩相同的,按照他们的数学成绩再次进行排名:0、2 同学对应的数学成绩分别为:9 和 0,从小到大再次排序,即 0 排在 2 的后面,对应的学生姓名为 c3 = [2,0,4,3,5,6,1]3、5、6 同学对应的数学成绩分别为:4、2、1,从小到大再次排序后,对应的学生姓名为 c4 = [2,0,4,6,5,3,1],即最终结果。简单来说,先对数组 a 从小到大排序,提取排序后元素对应的索引值,排序后元素有相同的,再根据数组 b 从小到大排序,得到最终的索引值。以上步骤用 numpy.lexsort() 函数实现如下:12345678910111213141516>>> import numpy as np>>> a = [1,5,1,4,3,4,4]>>> b = [9,4,0,4,0,2,1]>>> >>> c1 = np.lexsort((a,b)) # 先按 b 排序,再按照 a 排序>>> c2 = np.lexsort((b,a)) # 先按 a 排序,再按照 b 排序>>> >>> print(c1)[2 4 6 5 3 1 0]>>> print(c2)[2 0 4 6 5 3 1]>>> >>> print([(a[i],b[i]) for i in c1])[(1, 0), (3, 0), (4, 1), (4, 2), (4, 4), (5, 4), (1, 9)]>>> print([(a[i],b[i]) for i in c2])[(1, 0), (1, 9), (3, 0), (4, 1), (4, 2), (4, 4), (5, 4)]123456>>> import numpy as np>>> first_names = ('Hertz', 'Galilei', 'Hertz')>>> second_names = ('Heinrich', 'Galileo', 'Gustav')>>> >>> print(np.lexsort((second_names, first_names))) # 按照字母对应的 ascii 码从小到大进行排序[1 2 0]### 【5x05】sort_complex() numpy.sort_complex() 函数先使用实部,再使用虚部对复数数组进行排序。 复数:把形如 z=a+bi(a,b 均为实数)的数称为复数,其中 a 称为实部,b 称为虚部,i 称为虚数单位。 基本语法:numpy.sort_complex(a) 应用举例: 123456>>> import numpy as np>>> print(np.sort_complex([5, 3, 6, 2, 1]))[1.+0.j 2.+0.j 3.+0.j 5.+0.j 6.+0.j]>>> >>> print(np.sort_complex([1 + 2j, 2 - 1j, 3 - 2j, 3 - 3j, 3 + 5j]))[1.+2.j 2.-1.j 3.-3.j 3.-2.j 3.+5.j] 【5x06】partition()numpy.partition() 函数用于对数组进行分区排序,返回数组的分区副本。 基本语法:numpy.partition(a, kth[, axis=-1, kind='introselect', order=None]) 参数 描述 a 待排序数组 kth 整数或者整数序列,原数组元素中从小到大的第 k 个元素,在排序后的数组中,仍然处于第 k 位置小于该元素的位于该元素的左边,大于该元素的位于该元素的右边,左右两边没有特别的排序要求 axis 指定要排序的轴,可选项,默认对最后一个轴进行排序 kind 排序算法,可选项,默认值为快速排序(quicksort) order 字符串或者字符串列表,可选项,若指定 order 值,将按照该字段进行排序 应用举例: 1234567>>> import numpy as np>>> a = np.array([3, 4, 2, 1])>>> print(np.partition(a, 3)) # 原数组元素中从小到大的第 3 个元素,排序后仍然处于第 3 位置,小于 3 的在前面,大于 3 的在后面,前后无特别排序要求[2 1 3 4]>>> >>> print(np.partition(a, (1, 3))) # 小于 1 的在前面,大于 3 的在后面,1 和 3 之间的在中间[1 2 3 4] 【5x07】argpartition()numpy.argpartition() 函数用于对数组进行分区排序,返回重组后的索引值数组。 利用该函数可以很快地找出第 k 大的数的位置,以及大于 k(排在 k 后面)和小于 k(排在 k 前面)的数的位置。 基本语法:numpy.argpartition(a[, kth, axis=-1, kind='introselect', order=None]) 参数 描述 a 待排序数组 kth 整数或者整数序列,原数组元素中从小到大的第 k 个元素,在排序后的数组中,仍然处于第 k 位置小于该元素的位于该元素的左边,大于该元素的位于该元素的右边,左右两边没有特别的排序要求 axis 指定要排序的轴,可选项,默认对最后一个轴进行排序 kind 排序算法,可选项,默认值为快速排序(quicksort) order 字符串或者字符串列表,可选项,若指定 order 值,将按照该字段进行排序 以下实例将找到数组的第 3 小(索引值 2)的值和第 2 大(索引值 -2)的值: 123456>>> import numpy as np>>> a = np.array([46, 57, 23, 39, 1, 10, 0, 120])>>> print(a[np.argpartition(a, 2)[2]])10>>> print(a[np.argpartition(a, -2)[-2]])57 【5x08】unique()numpy.unique() 函数找到唯一值并返回从小到大的排序结果,类似于 Python 的 set 集合。 应用举例: 12345678>>> import numpy as np>>> print(np.unique([1, 1, 2, 2, 3, 3]))[1 2 3]>>> >>> a = np.array([[1, 0, 0], [1, 0, 0], [2, 3, 4]])>>> print(np.unique(a, axis=0))[[1 0 0] [2 3 4]] 【6x00】NumPy 条件函数【6x01】nonzero()numpy.nonzero() 函数将返回原数组中非零元素的索引值。 基本语法:numpy.nonzero(a) 应用举例: 12345>>> import numpy as np>>> a = np.array([[3, 0, 0], [0, 4, 0], [5, 6, 0]])>>> print(np.nonzero(a))(array([0, 1, 2, 2], dtype=int32), array([0, 1, 0, 1], dtype=int32)) 返回两个数组,其中的元素一一对应,比如数字 3 的索引值为 (0,0),数字 4 的索引值为 (1,1) 【6x02】where()numpy.where() 函数返回数组中满足指定条件的元素的索引值。 基本语法:numpy.where(condition[, x, y]) 参数 描述 condition 判断条件,如果满足该条件,则产生 x,否则产生 y,若未指定 x 与 y,则返回满足该条件元素的索引值 x, y 返回的值为数组对象,如果满足 condition,则产生 x,否则产生 y 应用举例: 12345678>>> import numpy as np>>> a = np.array([12, 7, 9, 10, 21, 5, 11, 1])>>>>>> print(np.where(a>8))(array([0, 2, 3, 4, 6], dtype=int32),) # 返回满足 a>8 的元素的索引值>>> >>> print(np.where(a>8, a, 10*a)) # 如果原数组中的元素 a>8,则返回 a,否则返回 10*a[12 70 9 10 21 50 11 10] 【6x03】extract()numpy.extract() 函数返回数组中满足指定条件的元素。 基本语法:numpy.extract(condition, arr) 参数 描述 condition 判断条件 arr 原数组 应用举例: 1234>>> import numpy as np>>> a = np.array([12, 7, 9, 10, 21, 5, 11, 1])>>> print(np.extract(a>8, a))[12 9 10 21 11] 123456789101112131415>>> import numpy as np>>> a = np.arange(9).reshape(3,3)>>> print(a)[[0 1 2] [3 4 5] [6 7 8]]>>> >>> condition = np.mod(a,2) == 0 # 定义筛选条件(余数为 0,即偶数)>>> print(condition)[[ True False True] [False True False] [ True False True]]>>>>>> print(np.extract(condition, a))[0 2 4 6 8] 【7x00】NumPy 判断函数【7x01】any() / all()numpy.any():如果至少有一个元素满足指定条件,则返回 True,否则返回 False。 numpy.all():如果所有的元素满足指定条件,则返回 True,否则返回 False。 基本语法:numpy.any(a[, axis=None, out=None, keepdims=<no value>]);numpy.all(a[, axis=None, out=None, keepdims=<no value>]) 参数 描述 a 待处理的数组对象 axis 指定轴,可选项,整数或者整数元组类型 out 可选项,放置结果的备用输出数组,必须具有与预期输出数组相同的形状和缓冲区长度 keepdims bool 类型,可选项,是否保持数组的二维特性 应用举例: 1234567>>> import numpy as np>>> print(np.any([[True, False], [True, True]]))True>>> >>> a = np.array([-3, -2, 4, 2, 8, 1])>>> print(np.any(a<0))True 1234567>>> import numpy as np>>> print(np.all([[True, False], [True, True]]))False>>> >>> a = np.array([-3, -2, 4, 2, 8, 1])>>> print(np.all(a<0))False 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105398131未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"NumPy","slug":"Python-数据分析/NumPy","permalink":"https://www.itrhx.com/categories/Python-数据分析/NumPy/"}],"tags":[{"name":"NumPy","slug":"NumPy","permalink":"https://www.itrhx.com/tags/NumPy/"},{"name":"数学函数","slug":"数学函数","permalink":"https://www.itrhx.com/tags/数学函数/"},{"name":"算术函数","slug":"算术函数","permalink":"https://www.itrhx.com/tags/算术函数/"},{"name":"统计函数","slug":"统计函数","permalink":"https://www.itrhx.com/tags/统计函数/"},{"name":"排序函数","slug":"排序函数","permalink":"https://www.itrhx.com/tags/排序函数/"},{"name":"条件函数","slug":"条件函数","permalink":"https://www.itrhx.com/tags/条件函数/"},{"name":"判断函数","slug":"判断函数","permalink":"https://www.itrhx.com/tags/判断函数/"}]},{"title":"Python 数据分析三剑客之 NumPy(四):字符串函数总结与对比","slug":"A65-NumPy-04","date":"2020-03-26T11:11:14.626Z","updated":"2020-07-06T13:28:08.067Z","comments":true,"path":"2020/03/26/A65-NumPy-04/","link":"","permalink":"https://www.itrhx.com/2020/03/26/A65-NumPy-04/","excerpt":"","text":"NumPy 系列文章: Python 数据分析三剑客之 NumPy(一):理解 NumPy / 数组基础 Python 数据分析三剑客之 NumPy(二):数组索引 / 切片 / 广播 / 拼接 / 分割 Python 数据分析三剑客之 NumPy(三):数组的迭代与位运算 Python 数据分析三剑客之 NumPy(四):字符串函数总结与对比 Python 数据分析三剑客之 NumPy(五):数学 / 算术 / 统计 / 排序 / 条件 / 判断函数合集 Python 数据分析三剑客之 NumPy(六):矩阵 / 线性代数库与 IO 操作 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105350414未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【01】NumPy 字符串函数速查表和 Python 一样,NumPy 也可以进行字符串相关操作。字符串函数在字符数组类(numpy.char)中定义。 NumPy 字符串函数速查表 函数 描述 add() 对两个数组的字符串元素进行连接 join() 通过指定分隔符来连接数组中的元素 mod() 格式化字符串,相当于 Python 字符串中的 % 和 format multiply() 按照给定值返回元素多重连接后的字符串 capitalize() 将字符串(字符串可同时包含字母和数字,只要是连续的都会被视为一个同字符串)第一个字母转换为大写 title() 将单词(仅包含字母,若同时包含数字和字母,则数字之后元素被视为另一个单词)第一个字母转换为大写 lower() 将数组中所有的元素转换为小写 upper() 将数组中所有的元素转换为大写 swapcase() 将数组中每个元素字母大写转为小写,小写转为大写 center() 居中字符串,并使用指定字符在左右侧进行填充 ljust() 左对齐字符串,并使用指定字符在右侧进行填充 rjust() 右对齐字符串,并使用指定字符在左侧进行填充 zfill() 在数组元素的左边填充指定个数的数字 0 strip() 移除数组每个元素开头和者结尾处的特定字符 lstrip() 移除数组每个元素开头(最左边)的特定字符 rstrip() 移除数组每个元素结尾(最右边)的特定字符 partition() 指定分割符对字符串进行分割(从最左边的分割符开始分割,仅分割一次,返回三个元素) rpartition() 指定分割符对字符串进行分割(从最右边的分割符开始分割,仅分割一次,返回三个元素) split() 指定分割符对字符串进行分割(从最左边的分割符开始分割,可指定分割次数,返回多个元素) rsplit() 指定分割符对字符串进行分割(从最右边的分割符开始分割,可指定分割次数,返回多个元素) replace() 使用新字符串替换原字符串中的子字符串 splitlines() 以换行符作为分隔符来分割字符串 translate() 将数组元素字符串按照给定的转换表进行映射 encode() 编码操作,数组元素依次调用 str.encode decode() 解码操作,数组元素依次调用 str.decode 【02】numpy.char.add()numpy.char.add() 函数用于对两个数组的字符串元素进行连接。 基本语法:numpy.char.add(x1, x2),数组 x1 和 x2 必须具有相同的形状。 参数 描述 x1 要处理的 str 或 unicode 数组 x2 要处理的 str 或 unicode 数组 应用举例: 12345>>> import numpy as np>>> print(np.char.add(['hello'],[' world']))['hello world']>>> print(np.char.add(['123', 'abc'], [' 456', ' def']))['123 456' 'abc def'] 【03】numpy.char.join()numpy.char.join() 函数通过指定分隔符来连接数组中的元素。 基本语法:numpy.char.join(sep1, seq2) 参数 描述 seq1 分割符,str 或 unicode 数组 seq2 被分割的 str 或 unicode 数组 应用举例: 123456>>> import numpy as np>>> print(np.char.join('-', 'python'))p-y-t-h-o-n>>>>>> print(np.char.join(['+','-'],['python','java']))['p+y+t+h+o+n' 'j-a-v-a'] 【04】numpy.char.mod()numpy.char.mod() 函数用于格式化字符串,相当于 Python 字符串中的 % 和 format。 基本语法:numpy.char.mod(value , a) 1234567>>> import numpy as np>>> print(np.char.mod('value=%.2f', np.arange(6)))['value=0.00' 'value=1.00' 'value=2.00' 'value=3.00' 'value=4.00' 'value=5.00']>>>>>> print(np.char.mod('value=%.4f', [[1.1, 2, 3.021], [4.12, 5, 6.1]]))[['value=1.1000' 'value=2.0000' 'value=3.0210'] ['value=4.1200' 'value=5.0000' 'value=6.1000']] 【05】numpy.char.multiply()numpy.char.multiply() 函数用于元素的多重连接,即返回 a*i。 基本语法:numpy.char.multiply(a, i) 参数 描述 a 要处理的 str 或 unicode 数组 i 整数数组 应用举例: 123>>> import numpy as np>>> print(np.char.multiply('Python ', 4))Python Python Python Python 【06】numpy.char.capitalize()numpy.char.capitalize() 函数将字符串第一个字母转换为大写。 基本语法:numpy.char.capitalize(a) 参数解释:a:要处理的 str 或 unicode 数组。 应用举例: 12345>>> import numpy as np>>> print(np.char.capitalize('python'))Python>>> print(np.char.capitalize(['a1b2','1b2a','b2a1','2a1b']))['A1b2' '1b2a' 'B2a1' '2a1b'] 【07】numpy.char.title()numpy.char.title() 函数将数组元素字符串的每个单词的第一个字母转换为大写。注意:如果一个字符串中间有非字母,则非字母之后的字符串会被视为另一个单词。 基本语法:numpy.char.title(a) 应用举例: 1234567>>> import numpy as np>>> print(np.char.title('i love python!'))I Love Python!>>> print(np.char.title('a1bc2def3h'))A1Bc2Def3H>>> print(np.char.title(['a1bc', 'a 1bc', 'a1 bc', 'a1b c']))['A1Bc' 'A 1Bc' 'A1 Bc' 'A1B C'] 【08】numpy.char.lower()numpy.char.lower() 函数将数组中所有的元素转换为小写。 基本语法:numpy.char.lower(a) 应用举例: 12345>>> import numpy as np>>> print(np.char.lower('PYTHON'))python>>> print(np.char.lower(['PYTHON', 'A123C', 'Ba1A']))['python' 'a123c' 'ba1a'] 【09】numpy.char.upper()numpy.char.upper() 函数将数组中所有的元素转换为大写。 基本语法:numpy.char.upper(a) 应用举例: 12345>>> import numpy as np>>> print(np.char.upper('python'))PYTHON>>> print(np.char.upper(['python', 'a123c', 'ba1A']))['PYTHON' 'A123C' 'BA1A'] 【10】numpy.char.swapcase()numpy.char.swapcase() 函数将数组中每个元素字母大写转为小写,小写转为大写。 基本语法:numpy.char.swapcase(a) 应用举例: 12345>>> import numpy as np>>> print(np.char.swapcase('Abc123DEf456gHI'))aBC123deF456Ghi>>> print(np.char.swapcase(['Abc', '1De', '23F', 'Ghi']))['aBC' '1dE' '23f' 'gHI'] 【11】numpy.char.center()numpy.char.center() 函数用于居中字符串,并使用指定字符在左右侧进行填充。 基本语法:numpy.char.center(a, width[, fillchar=' ']) 参数 描述 a 要处理的 str 或 unicode 数组 width int 类型,结果字符串的总长度 fillchar 可选项,str 或 unicode 数组,要使用的填充字符,默认为空格 应用举例: 1234567>>> import numpy as np>>> print(np.char.center('python', 10)) python >>> print(np.char.center('python', 12, fillchar='-'))---python--->>> print(np.char.center('python', 11, fillchar='-'))---python-- 【12】numpy.char.ljust()numpy.char.ljust() 函数用于左对齐字符串,并使用指定字符在右侧进行填充。 基本语法:numpy.char.ljust(a, width[, fillchar=' ']) 参数 描述 a 要处理的 str 或 unicode 数组 width int 类型,结果字符串的总长度 fillchar 可选项,str 或 unicode 数组,要使用的填充字符,默认为空格 应用举例: 123>>> import numpy as np>>> print(np.char.ljust('python', 10, fillchar='-'))python---- 【13】numpy.char.rjust()numpy.char.ljust() 函数用于右对齐字符串,并使用指定字符在左侧进行填充。 基本语法:numpy.char.rjust(a, width[, fillchar=' ']) 参数 描述 a 要处理的 str 或 unicode 数组 width int 类型,结果字符串的总长度 fillchar 可选项,str 或 unicode 数组,要使用的填充字符,默认为空格 应用举例: 123>>> import numpy as np>>> print(np.char.rjust('python', 10, fillchar='-'))----python 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105350414未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【14】numpy.char.zfill()numpy.char.zfill() 函数在数组元素的左边填充指定个数的数字 0。 基本语法:numpy.char.zfill(a, width) 参数 描述 a 要处理的 str 或 unicode 数组 width int 类型,数组字符串在左边填充 0 后整个字符串的宽度如果宽度小于原字符串的宽度,则结果会去掉原字符串中多余的元素 应用举例: 12345>>> import numpy as np>>> print(np.char.zfill('python', 3))pyt>>> print(np.char.zfill('python', 10))0000python 【15】numpy.char.strip()numpy.char.strip() 函数用于移除开头和结尾处的特定字符。 基本语法:numpy.char.strip(a[, chars=None]) 参数 描述 a 要处理的 str 或 unicode 数组 chars 可选项,str 类型,指定要删除的字符集,如果省略或者为 None,则默认为删除空白 应用举例: 123456>>> import numpy as np>>> print(np.char.strip('alibaba','a'))libab>>> >>> print(np.char.strip(['Alibaba','admin','java', 'ABBA'],'a'))['Alibab' 'dmin' 'jav' 'ABBA'] 【16】numpy.char.lstrip()numpy.char.lstrip() 函数用于移除数组每个元素最右边的特定字符。 基本语法:numpy.char.lstrip(a[, chars=None]) 参数 描述 a 要处理的 str 或 unicode 数组 chars 可选项,str 类型,指定要删除的字符集,如果省略或者为 None,则默认为删除空白 应用举例: 123456>>> import numpy as np>>> print(np.char.lstrip('alibaba','a'))libaba>>> >>> print(np.char.lstrip(['Alibaba','admin','java', 'aBBa'],'a'))['Alibaba' 'dmin' 'java' 'BBa'] 【17】numpy.char.rstrip()numpy.char.rstrip() 函数用于移除数组每个元素最右边的特定字符。 基本语法:numpy.char.rstrip(a[, chars=None]) 参数 描述 a 要处理的 str 或 unicode 数组 chars 可选项,str 类型,指定要删除的字符集,如果省略或者为 None,则默认为删除空白 应用举例: 12345>>> import numpy as np>>> print(np.char.rstrip('alibaba','a'))alibab>>> print(np.char.rstrip(['Alibaba','admin','java', 'aBBa'],'a'))['Alibab' 'admin' 'jav' 'aBB'] 【18】numpy.char.partition()numpy.char.partition() 函数通过指定分割符对字符串进行分割,从最左边第一次出现的分割符开始分割,仅分割一次,返回三个元素。 基本语法:numpy.char.partition(a, sep) 参数 描述 a 要处理的 str 或 unicode 数组 sep 分割字符,str 或 unicode 类型,返回三个元素:分割字符前的字符,分割字符,分割字符后的字符如果元素包含多个分割字符,以最左边的为准,如果找不到分隔符,则返回三个元素:字符串本身以及两个空字符串 应用举例: 123456789>>> print(np.char.partition('111a222','a'))['111' 'a' '222']>>> print(np.char.partition('111a222a333','a'))['111' 'a' '222a333']>>> print(np.char.partition('111a222a333','b'))['111a222a333' '' '']>>> print(np.char.partition(['111a222', '23a45'],'a'))[['111' 'a' '222'] ['23' 'a' '45']] 【19】numpy.char.rpartition()numpy.char.partition() 函数通过指定分割符对字符串进行分割,从最右边第一次出现的分割符开始分割,仅分割一次,返回三个元素。 基本语法:numpy.char.rpartition(a, sep) 参数 描述 a 要处理的 str 或 unicode 数组 sep 分割字符,str 或 unicode 类型,返回三个元素:分割字符前的字符,分割字符,分割字符后的字符如果元素包含多个分割字符,以最右边的为准,如果找不到分隔符,则返回三个元素:两个空字符串以及字符串本身 应用举例: 12345678>>> import numpy as np>>> print(np.char.rpartition('111a222a333','a'))['111a222' 'a' '333']>>> print(np.char.rpartition('111a222a333','b'))['' '' '111a222a333']>>> print(np.char.rpartition(['111a222a333', '23a45'],'a'))[['111a222' 'a' '333'] ['23' 'a' '45']] 【20】numpy.char.split()numpy.char.split() 函数通过指定分割符对字符串进行分割,从最左边的分割符开始分割,可指定分割次数,返回多个元素。 基本语法:numpy.char.split(a[, sep=None, maxsplit=None]) 参数 描述 a 要处理的 str 或 unicode 数组 sep 分隔符,可选项,str 或者 unicode 类型,如果 sep 未指定或者为 None,则默认为空格 maxsplit 可选项,int 类型,如果指定 maxsplit,则最多完成 maxsplit 次分割 应用举例: 123456789>>> import numpy as np>>> print(np.char.split('I love python!'))['I', 'love', 'python!']>>> print(np.char.split('www.itrhx.com', sep='.'))['www', 'itrhx', 'com']>>> print(np.char.split('one.two.itrhx.com', sep='.', maxsplit=2))['one', 'two', 'itrhx.com']>>> print(np.char.split('one.two.itrhx.com', '.', 2))['one', 'two', 'itrhx.com'] 【21】numpy.char.rsplit()numpy.char.split() 函数通过指定分割符对字符串进行分割,从最右边的分割符开始分割,可指定分割次数,返回多个元素。 基本语法:numpy.char.rsplit(a[, sep=None, maxsplit=None]) 参数 描述 a 要处理的 str 或 unicode 数组 sep 分隔符,可选项,str 或者 unicode 类型,如果 sep 未指定或者为 None,则默认为空格 maxsplit 可选项,int 类型,如果指定 maxsplit,则最多完成 maxsplit 次分割 应用举例: 123>>> import numpy as np>>> print(np.char.rsplit('one.two.itrhx.com', '.', 2))['one.two', 'itrhx', 'com'] 【22】numpy.char.replace()numpy.char.replace() 函数可以使用新字符串来替换原字符串中的子字符串。 基本语法:numpy.char.replace(a, old, new[, count=None]) 参数 描述 a 要处理的 str 或 unicode 数组 old 旧的字符串,即要替换的字符串,str 或 unicode 类型 new 新的字符串,即替换的字符串,str 或 unicode 类型 count int 类型,如果指定该值 N,则会替换 old 中出现的前 N 个字符串 应用举例: 123456789>>> import numpy as np>>> print(np.char.replace('i like python', 'python', 'java'))i like java>>> >>> print(np.char.replace('aaaaaaa', 'a', 'b', count=3))bbbaaaa>>>>>> print(np.char.replace('a111a11a1a111aa', 'a', 'A', count=3))A111A11A1a111aa 【23】numpy.char.splitlines()numpy.char.splitlines() 函数以换行符作为分隔符来分割字符串,并返回数组。 基本语法:numpy.char.splitlines(a[, keepends=None]) 参数 描述 a 要处理的 str 或 unicode 数组 keepends 如果指定 keepends 为 True,则换行符会包含在结果列表中,否则不包含 12345>>> import numpy as np>>> print(np.char.splitlines('hi python!\\nhi java!'))['hi python!', 'hi java!']>>> print(np.char.splitlines('hi python!\\nhi java!', keepends=True))['hi python!\\n', 'hi java!'] 【24】numpy.char.translate()numpy.char.translate() 函数将数组元素字符串按照给定的转换表进行映射。 基本语法:numpy.char.translate(a, table[, deletechars=None]) 参数 描述 a 要处理的 str 或 unicode 数组 table 包含 256 个字符的映射表,映射表通过 str.maketrans() 方法转换而来 deletechars 可选项,str 类型,字符串中要过滤的字符列表 应用举例: 123456>>> import numpy as np>>> intab = 'abcdef'>>> outtab = '123456'>>> table = str.maketrans(intab, outtab) # 制作映射表>>> print(np.char.translate('this is a translate example!', table))this is 1 tr1nsl1t5 5x1mpl5! 【25】numpy.char.encode()numpy.char.encode() 函数用于编码操作,数组元素依次调用 str.encode,可以使用 Python 标准库中的编解码器。 基本语法:numpy.char.encode(a[, encoding=None, errors=None]) 参数 描述 a 要处理的 str 或 unicode 数组 encoding 编码名称,可选项,str 类型,默认编码为 utf-8 errors 指定如何处理编码错误,可选项,str 类型 应用举例: 123456>>> import numpy as np>>> print(np.char.encode('python', 'cp500'))b'\\x97\\xa8\\xa3\\x88\\x96\\x95'>>>>>> print(np.char.encode(['aAaAaA', ' aA ', 'abBABba'], 'cp037'))[b'\\x81\\xc1\\x81\\xc1\\x81\\xc1' b'@@\\x81\\xc1@@' b'\\x81\\x82\\xc2\\xc1\\xc2\\x82\\x81'] 【26】numpy.char.decode()numpy.char.decode() 函数用于解码操作,数组元素依次调用 str.decode,可以使用 Python 标准库中的编解码器。 基本语法:numpy.char.decode(a[, encoding=None, errors=None]) 参数 描述 a 要处理的 str 或 unicode 数组 encoding 编码名称,可选项,str 类型,默认编码为 utf-8 errors 指定如何处理编码错误,可选项,str 类型 应用举例: 123456>>> import numpy as np>>> print(np.char.decode(b'\\x97\\xa8\\xa3\\x88\\x96\\x95', 'cp500'))python>>>>>> print(np.char.decode([b'\\x81\\xc1\\x81\\xc1\\x81\\xc1' b'@@\\x81\\xc1@@' b'\\x81\\x82\\xc2\\xc1\\xc2\\x82\\x81'], 'cp500'))['aAaAaA aA abBABba'] 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105350414未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"NumPy","slug":"Python-数据分析/NumPy","permalink":"https://www.itrhx.com/categories/Python-数据分析/NumPy/"}],"tags":[{"name":"NumPy","slug":"NumPy","permalink":"https://www.itrhx.com/tags/NumPy/"},{"name":"字符串函数","slug":"字符串函数","permalink":"https://www.itrhx.com/tags/字符串函数/"}]},{"title":"Python 数据分析三剑客之 NumPy(三):数组的迭代与位运算","slug":"A64-NumPy-03","date":"2020-03-24T10:16:43.374Z","updated":"2020-08-06T03:04:30.457Z","comments":true,"path":"2020/03/24/A64-NumPy-03/","link":"","permalink":"https://www.itrhx.com/2020/03/24/A64-NumPy-03/","excerpt":"","text":"NumPy 系列文章: Python 数据分析三剑客之 NumPy(一):理解 NumPy / 数组基础 Python 数据分析三剑客之 NumPy(二):数组索引 / 切片 / 广播 / 拼接 / 分割 Python 数据分析三剑客之 NumPy(三):数组的迭代与位运算 Python 数据分析三剑客之 NumPy(四):字符串函数总结与对比 Python 数据分析三剑客之 NumPy(五):数学 / 算术 / 统计 / 排序 / 条件 / 判断函数合集 Python 数据分析三剑客之 NumPy(六):矩阵 / 线性代数库与 IO 操作 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105185337未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【1x00】numpy.nditer 迭代器对象numpy.nditer 是 NumPy 的迭代器对象,迭代器对象提供了许多灵活的方法来访问一个或多个数组中的所有元素,简单来说,迭代器最基本的任务就是完成对数组元素的访问。 【1x01】单数组的迭代单数组迭代示例: 12345678910>>> import numpy as np>>> a = np.arange(10).reshape(2, 5)>>> print(a)[[0 1 2 3 4] [5 6 7 8 9]]>>> for i in np.nditer(a): print(i, end=' ') 0 1 2 3 4 5 6 7 8 9 注意:默认对数组元素的访问顺序,既不是以标准 C(行优先) 也不是 Fortran 顺序(列优先),选择的顺序是和数组内存布局一致的,这样做是为了提高访问效率,反映了默认情况下只需要访问每个元素而不关心特定排序的想法。以下用一个数组的转置来理解这种访问机制。 123456789101112131415161718192021222324252627282930313233343536373839>>> import numpy as np>>> a = np.arange(10).reshape(2, 5)>>> print(a)[[0 1 2 3 4] [5 6 7 8 9]]>>> >>> b = a.T>>> print(b)[[0 5] [1 6] [2 7] [3 8] [4 9]]>>> >>> c = a.T.copy(order='C')>>> print(c)[[0 5] [1 6] [2 7] [3 8] [4 9]]>>> >>> for i in np.nditer(a): print(i, end=' ') 0 1 2 3 4 5 6 7 8 9 >>> >>> for i in np.nditer(b): print(i, end=' ') 0 1 2 3 4 5 6 7 8 9 >>> >>> for i in np.nditer(c): print(i, end=' ') 0 5 1 6 2 7 3 8 4 9 例子中 a 是一个 2 行 5 列的数组,b 数组对 a 进行了转置,而 c 数组则是对 a 进行转置后按照 C order(行优先)的形式复制到新内存中储存,b 数组虽然进行了转置操作,但是其元素在内存当中的储存顺序仍然和 a 一样,所以对其迭代的效果也和 a 一样,c 数组元素在新内存当中的储存顺序不同于 a 和 b,因此对其迭代的效果也不一样。 【1x02】控制迭代顺序如果想要按照特定顺序来对数组进行迭代,nditer 同样也提供了 order 参数,可选值为:C F A K numpy.nditer(a, order='C'):标准 C 顺序,即行优先; numpy.nditer(a, order='F'): Fortran 顺序,即列优先; numpy.nditer(a, order='A'):如果所有数组都是 Fortran 顺序的,则 A 表示以 F 顺序,否则以 C 顺序; numpy.nditer(a, order='K'):默认值,保持原数组在内存当中的顺序。 应用举例: 123456789101112131415161718192021222324>>> import numpy as np>>> a = np.arange(12).reshape(3, 4)>>> print(a)[[ 0 1 2 3] [ 4 5 6 7] [ 8 9 10 11]]>>> >>> for i in np.nditer(a, order='C'): print(i, end= ' ') 0 1 2 3 4 5 6 7 8 9 10 11 >>> >>> for i in np.nditer(a, order='F'): print(i, end= ' ') 0 4 8 1 5 9 2 6 10 3 7 11 >>> >>> for i in np.nditer(a, order='K'): print(i, end= ' ') 0 1 2 3 4 5 6 7 8 9 10 11 【1x03】修改数组元素nditer 对象提供了可选参数 op_flags,默认情况下,该参数值为 readonly(只读),如果在遍历数组的同时,要实现对数组中元素值的修改,则可指定 op_flags 值为 readwrite(读写) 或者 writeonly(只读)。 应用举例: 12345678910111213>>> import numpy as np>>> a = np.arange(10).reshape(2, 5)>>> print(a)[[0 1 2 3 4] [5 6 7 8 9]]>>> >>> for i in np.nditer(a, op_flags=['readwrite']): i[...] = i+1 >>> print(a)[[ 1 2 3 4 5] [ 6 7 8 9 10]] 1234567891011121314>>> import numpy as np>>> li = []>>> a = np.arange(12).reshape(3, 4)>>> print(a)[[ 0 1 2 3] [ 4 5 6 7] [ 8 9 10 11]]>>> >>> for i in np.nditer(a, op_flags=['readwrite']): li.append(i*2) >>> print(li)[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22] 【1x04】使用外部循环nditer 对象支持 flags 参数,该参数最常用的值是 external_loop,表示使给定的值为具有多个值的一维数组。 通俗来讲,当 Ndarray 的顺序和遍历的顺序一致时,就会将所有元素组成一个一维数组返回;当 Ndarray 的顺序和遍历的顺序不一致时,则返回每次遍历的一维数组 官方介绍:https://docs.scipy.org/doc/numpy/reference/arrays.nditer.html#using-an-external-loop 应用举例: 123456789101112>>> import numpy as np>>> a = np.array([[0,1,2,3], [4,5,6,7], [8,9,10,11]], order='C')>>> for i in np.nditer(a, flags=['external_loop'], order='C' ): print(i, end=' ') [ 0 1 2 3 4 5 6 7 8 9 10 11] >>> for i in np.nditer(a, flags=['external_loop'], order='F' ): print(i, end=' ') [0 4 8] [1 5 9] [ 2 6 10] [ 3 7 11] 【1x05】跟踪元素索引在迭代期间,我们有可能希望在计算中使用当前元素的索引值,同样可以通过指定 flags 参数的取值来实现: 参数 描述 c_index 跟踪 C 顺序索引 f_index 跟踪 Fortran 顺序索引 multi_index 跟踪多个索引或每个迭代维度一个索引的元组(多重索引) 在以下实例当中: 当参数为 c_index 和 f_index 时,it.index 用于输出元素的索引值 当参数为 multi_index 时,it.multi_index 用于输出元素的索引值 it.iternext() 表示进入下一次迭代,直到迭代完成为止 multi_index 可理解为对迭代对象进行多重索引 1234567891011121314151617181920>>> import numpy as np>>> a = np.arange(6).reshape(2, 3)>>> it = np.nditer(a, flags=['c_index'])>>> while not it.finished: print('%d <%s>' %(it[0], it.index)) it.iternext() 0 <0>True1 <1>True2 <2>True3 <3>True4 <4>True5 <5>False 1234567891011121314151617181920>>> import numpy as np>>> a = np.arange(6).reshape(2, 3)>>> it = np.nditer(a, flags=['f_index'])>>> while not it.finished: print('%d <%s>' %(it[0], it.index)) it.iternext() 0 <0>True1 <2>True2 <4>True3 <1>True4 <3>True5 <5>False 1234567891011121314151617181920>>> import numpy as np>>> a = np.arange(6).reshape(2, 3)>>> it = np.nditer(a, flags=['multi_index'])>>> while not it.finished: print('%d <%s>' %(it[0], it.multi_index)) it.iternext() 0 <(0, 0)>True1 <(0, 1)>True2 <(0, 2)>True3 <(1, 0)>True4 <(1, 1)>True5 <(1, 2)>False 【1x06】广播数组迭代如果两个数组满足广播原则,nditer 对象能够同时迭代它们,即广播数组迭代(多数组的迭代)。 123456789101112131415161718>>> import numpy as np>>> a = np.arange(3)>>> b = np.arange(6).reshape(2,3)>>> print(a)[0 1 2]>>> print(b)[[0 1 2] [3 4 5]]>>> for m, n in np.nditer([a,b]): print(m,n) 0 01 12 20 31 42 5 如果两个数组不满足广播原则,将会抛出异常: 12345678910111213141516>>> import numpy as np>>> a = np.arange(4)>>> b = np.arange(6).reshape(2,3)>>> print(a)[0 1 2 3]>>> print(b)[[0 1 2] [3 4 5]]>>> for m, n in np.nditer([a,b]): print(m,n) Traceback (most recent call last): File \"<pyshell#55>\", line 1, in <module> for m, n in np.nditer([a,b]):ValueError: operands could not be broadcast together with shapes (4,) (2,3) 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105185337未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【2x00】NumPy 位运算由于位运算是直接对整数在内存中的二进制位进行操作,所以有必要先了解一下如何来表示数字的二进制。 在 Python 中,提供了一个内置函数 bin(),将整数转换为以 0b 为前缀的二进制字符串,如果要去掉 0b 前缀,则可以使用 format 方法,因为返回的是字符串,所以也可以使用切片等其他方法去掉前缀。 123456>>> bin(3)'0b11'>>> format(3, 'b')'11'>>> f'{3:b}''11' 除了内置函数以外,NumPy 还提供了一个 numpy.binary_repr 函数,该函数的作用也是以字符串形式返回输入数字的二进制表示形式。 基本语法:numpy.binary_repr(num, width=None) 参数解释: 参数 描述 num 要表示的数,只能是整数形式 width 可选项,对于负数,如果未指定 width,则会在前面添加减号,如果指定了 width,则返回该宽度的负数的二进制补码 123456789>>> import numpy as np>>> np.binary_repr(3)'11'>>> np.binary_repr(-3)'-11'>>> np.binary_repr(-3, width=4)'1101'>>> np.binary_repr(3, width=4)'0011' 以下是 NumPy 数组当中用到的位运算函数,各函数与其对应的用操作符计算的作用相同。 函数 描述 操作符 bitwise_and 对数组元素进行按位与(AND)操作 & bitwise_or 对数组元素进行按位或(OR)操作 \\ bitwise_xor 对数组元素执行按位异或(XOR)操作 ^ invert 对数组元素执行按位取反(NOT)操作 ~ left_shift 将数组元素的二进制形式向左移动指定位,右侧附加相等数量的 0 << right_shift 将数组元素的二进制形式向右移动指定位,左侧附加相等数量的 0 >> 【2x01】numpy.bitwise_and()numpy.bitwise_and() 函数对数组元素进行按位与(AND)操作。 1234567>>> import numpy as np>>> np.binary_repr(10), np.binary_repr(15)('1010', '1111')>>> np.bitwise_and(10, 15)10>>> np.binary_repr(10)'1010' numpy.bitwise_and() 函数还支持多个元素同时进行按位与操作: 123>>> import numpy as np>>> np.bitwise_and([14,3], 13)array([12, 1], dtype=int32) 123456>>> import numpy as np>>> np.bitwise_and([11,7], [4,25])array([0, 1], dtype=int32)>>>>>> np.array([11,7]) & np.array([4,25]) # 函数与 & 操作符作用一样array([0, 1], dtype=int32) 还可以传入布尔值: 123>>> import numpy as np>>> np.bitwise_and([True,False,True],[True,True,True])array([ True, False, True]) 【2x02】numpy.bitwise_or()numpy.bitwise_or() 函数对数组元素进行按位或(OR)操作。 1234567>>> import numpy as np>>> np.binary_repr(10), np.binary_repr(14)('1010', '1110')>>> np.bitwise_or(10, 14)14>>> np.binary_repr(14)'1110' 和按位与操作一样,numpy.bitwise_or() 函数也支持传入布尔值和多个元素同时进行操作: 123456789101112131415>>> import numpy as np>>> np.bitwise_or([33,4], 1)array([33, 5], dtype=int32)>>>>>> np.bitwise_or([33,4], [1,2])array([33, 6], dtype=int32)>>>>>> np.bitwise_or(np.array([2,5,255]), np.array([4,4,4]))array([ 6, 5, 255], dtype=int32)>>>>>> np.array([2,5,255]) | np.array([4,4,4]) # 函数与 | 运算符作用相同array([ 6, 5, 255], dtype=int32)>>>>>> np.bitwise_or([True, True], [False,True])array([ True, True]) 【2x03】numpy.bitwise_xor()numpy.bitwise_xor() 函数对数组元素执行按位异或(XOR)操作。 1234567>>> import numpy as np>>> bin(13), bin(17)('0b1101', '0b10001')>>> np.bitwise_xor(13,17)28>>> bin(28)'0b11100' 123456789101112>>> import numpy as np>>> np.bitwise_xor([31,3], 5)array([26, 6], dtype=int32)>>> >>> np.bitwise_xor([31,3], [5,6])array([26, 5], dtype=int32)>>> >>> np.array([31,3]) ^ np.array([5,6]) # 函数与 ^ 运算符作用相同array([26, 5], dtype=int32)>>>>>> np.bitwise_xor([True, True], [False, True])array([ True, False]) 【2x04】numpy.invert()numpy.invert() 函数将对数组元素执行按位取反(NOT)操作,注意按位取反和取反操作不同。 按位取反通用公式:~x = -(x+1) 我们将原来的数称为 A,按位取反后的数称为 B,按位取反的步骤如下:先求 A 的补码,对 A 的补码每一位取反(包括符号位),得到的数为 B 的补码,将 B 的补码转换为 B 的原码得到最终结果。 分情况具体讨论: 正数按位取反步骤 1、将其转换成二进制形式;2、求其补码(正数的原码、反码、补码都相同);3、将补码每一位进行取反操作(包括符号位); 【经过步骤 3 后的结果为一个二进制形式的负数补码,接下来将这个负数补码转换成原码(负数原码到补码的逆运算)】4、对步骤 3 得到的负数 -1 得到反码;5、对步骤 4 得到的反码再进行取反得到原码;6、将步骤 5 得到的原码转回十进制即是最终结果。负数按位取反步骤1、将其转换成二进制形式;2、求其补码(先求其反码、符号位不变,末尾 +1 得到其补码);3、将补码每一位进行取反操作(包括符号位);【经过步骤 3 后的结果为一个二进制形式的正数,接下来将这个正数转换成原码即可】4、由于正数的原码、反码、补码都相同,所以直接将其转换成十进制即为最终结果。注意:第 3 步的取反操作,包括符号位都要取反,与求反码不同,求反码时符号位不变。 具体计算举例(二进制前 4 位为符号位): 9 的按位取反 ① 原码:0000 1001② 反码:0000 1001③ 补码:0000 1001④ 取反:1111 0110 (包括符号位一起取反,得到新的补码)⑤ 反码:1111 0101 (将新的补码 -1 得到其反码)⑥ 原码:1111 1010 (将反码取反得到原码)⑦ 转为十进制:-10 -9 的按位取反 ① 原码:1111 1001② 反码:1111 0110③ 补码:1111 0111④ 取反:0000 1000 (包括符号位一起取反,得到新的补码)⑤ 原码:0000 1000 (由于新的补码为正数,所以原码补码相同)⑥ 转为十进制:8 其他关于按位取反操作的知识: 按位取反运计算方法 [干货]按位取反怎么算?~图文详解 Python 代码应用示例: 1234567>>> import numpy as np>>> np.binary_repr(9, width=8)'00001001'>>> np.invert(9)-10>>> np.invert(-9)8 【2x05】numpy.left_shift()numpy.left_shift() 函数将数组元素的二进制形式向左移动指定位,右侧附加相等数量的 0。 应用举例: 123456789>>> import numpy as np>>> np.binary_repr(10, width=8)'00001010'>>> np.left_shift(10, 2)40>>> 10 << 2 # numpy.left_shift 函数相当于 Python 当中的 << 运算符40>>> np.binary_repr(40, width=8)'00101000' 【2x06】numpy.right_shift()numpy.right_shift() 函数将数组元素的二进制形式向右移动指定位,左侧附加相等数量的 0 123456789>>> import numpy as np>>> np.binary_repr(10, width=8)'00001010'>>> np.right_shift(10, 2)2>>> 10 >> 2 # numpy.right_shift 函数相当于 Python 当中的 >> 运算符2>>> np.binary_repr(2, width=8)'00000010' 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/105185337未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"NumPy","slug":"Python-数据分析/NumPy","permalink":"https://www.itrhx.com/categories/Python-数据分析/NumPy/"}],"tags":[{"name":"NumPy","slug":"NumPy","permalink":"https://www.itrhx.com/tags/NumPy/"},{"name":"迭代","slug":"迭代","permalink":"https://www.itrhx.com/tags/迭代/"},{"name":"位运算","slug":"位运算","permalink":"https://www.itrhx.com/tags/位运算/"}]},{"title":"Python 数据分析三剑客之 NumPy(二):数组索引 / 切片 / 广播 / 拼接 / 分割","slug":"A63-NumPy-02","date":"2020-03-22T07:16:24.367Z","updated":"2020-08-06T03:03:09.474Z","comments":true,"path":"2020/03/22/A63-NumPy-02/","link":"","permalink":"https://www.itrhx.com/2020/03/22/A63-NumPy-02/","excerpt":"","text":"NumPy 系列文章: Python 数据分析三剑客之 NumPy(一):理解 NumPy / 数组基础 Python 数据分析三剑客之 NumPy(二):数组索引 / 切片 / 广播 / 拼接 / 分割 Python 数据分析三剑客之 NumPy(三):数组的迭代与位运算 Python 数据分析三剑客之 NumPy(四):字符串函数总结与对比 Python 数据分析三剑客之 NumPy(五):数学 / 算术 / 统计 / 排序 / 条件 / 判断函数合集 Python 数据分析三剑客之 NumPy(六):矩阵 / 线性代数库与 IO 操作 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/104988137未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【1x00】认识 Numpy 中的 nan 和 infnan(NAN,Nan):全称 not a number,即不是一个数字inf(-inf,inf):全称 infinity,inf 表示正无穷,-inf 表示负无穷 以下情况 Numpy 中会出现 nan: 当读取本地的文件为 float 的时候,如果有缺失,就会出现 nan 当做了一个不合适的计算的时候(如:无穷大减去无穷大) 以下情况会出现 inf 或者 -inf: 比如一个数字除以 0,在 Python 中直接会报错,但在 Numpy 中则是一个 inf 或者 -inf 指定一个 nan 或者 inf: 1234567891011>>> import numpy as np>>> a = np.nan>>> b = np.inf>>> print(a)nan>>> print(b)inf>>> type(a)<class 'float'>>>> type(b)<class 'float'> 注意两个 nan 并不相等,而两个 inf 是相等的 1234>>> np.nan == np.nanFalse>>> np.inf == np.infTrue nan 和任何值计算都为 nan: 12345>>> import numpy as np>>> a = np.array([1, 2, 3, 4, 5])>>> b = np.array([1, 2, np.nan, 4, np.nan])>>> print(a+b)[ 2. 4. nan 8. nan] 【1x01】判断是否为 nan 和 inf isnan:判断元素是否为 nan(非数字) isinf:判断元素是否为正无穷大或负无穷大 isposinf:判断元素是否为正无穷大 isneginf:判断元素是否为负无穷大 isfinite:判断元素是否为有限的(不是非数字,正无穷大和负无穷大中的一个) 12345678910>>> import numpy as np>>> a = np.array([[1, 2, 3, 4], [np.nan, 6, 7, np.nan], [9 ,np.nan, 10, 11]])>>> print(a)[[ 1. 2. 3. 4.] [nan 6. 7. nan] [ 9. nan 10. 11.]]>>> np.isnan(a)array([[False, False, False, False], [ True, False, False, True], [False, True, False, False]]) 【1x02】统计数组中 nan 的个数1、利用 np.count_nonzero 方法,结合两个 nan 不相等的属性,可以判断数组中 nan 的个数: 12345678>>> import numpy as np>>> a = np.array([[1, 2, 3, 4], [np.nan, 6, 7, np.nan], [9 ,np.nan, 10, 11]])>>> print(a)[[ 1. 2. 3. 4.] [nan 6. 7. nan] [ 9. nan 10. 11.]]>>> np.count_nonzero(a != a) # 判断 nan 的个数,a != a 即为 nan,nan != nan3 2、isnan() 方法可以判断哪些是 nan,再结合 np.count_nonzero 方法可以判断数组中 nan 的个数: 123456789101112>>> import numpy as np>>> a = np.array([[1, 2, 3, 4], [np.nan, 6, 7, np.nan], [9 ,np.nan, 10, 11]])>>> print(a)[[ 1. 2. 3. 4.] [nan 6. 7. nan] [ 9. nan 10. 11.]]>>> np.isnan(a) # np.isnan(a) 与 a != a 效果相同,判断那些是 nanarray([[False, False, False, False], [ True, False, False, True], [False, True, False, False]])>>> np.count_nonzero(np.isnan(a))3 3、利用 collections 模块的 Counter 方法来统计 nan 的个数(此方法仅适用于一维数组): 12345>>> from collections import Counter>>> import numpy as np>>> a = np.array([1, 2, np.nan, 4, np.nan])>>> print(Counter(np.isnan(a)))Counter({False: 3, True: 2}) 【1x03】统计数组中 inf 的个数1、isinf() 方法可以判断哪些是 inf,再结合 np.count_nonzero 方法可以判断数组中 inf 的个数: 123456789101112>>> import numpy as np>>> a = np.array([[1, 2, 3, 4], [np.inf, 6, 7, -np.inf], [9 ,-np.inf, 10, 11]])>>> print(a)[[ 1. 2. 3. 4.] [ inf 6. 7. -inf] [ 9. -inf 10. 11.]]>>> np.isinf(a)array([[False, False, False, False], [ True, False, False, True], [False, True, False, False]])>>> np.count_nonzero(np.isinf(a))3 2、利用 collections 模块的 Counter 方法来统计 inf 的个数(此方法仅适用于一维数组): 12345>>> from collections import Counter>>> import numpy as np>>> a = np.array([1, 2, np.inf, 4, -np.inf])>>> print(Counter(np.isinf(a)))Counter({False: 3, True: 2}) 【1x04】替换 inf 和 nannumpy.nan_to_num() 方法可以将 nan 替换为零,将 inf 替换为有限数。 12345678910>>> import numpy as np>>> a = np.array([[1, np.nan, 3, 4], [np.inf, 6, 7, -np.inf], [9 ,-np.inf, 10, np.nan]])>>> print(a)[[ 1. nan 3. 4.] [ inf 6. 7. -inf] [ 9. -inf 10. nan]]>>> print(np.nan_to_num(a))[[ 1.00000000e+000 0.00000000e+000 3.00000000e+000 4.00000000e+000] [ 1.79769313e+308 6.00000000e+000 7.00000000e+000 -1.79769313e+308] [ 9.00000000e+000 -1.79769313e+308 1.00000000e+001 0.00000000e+000]] 如果要将 nan 和 inf 替换成特定的值,则可以用以下方法: 1234567891011121314>>> import numpy as np>>> a = np.array([[1, np.nan, 3, 4], [np.inf, 6, 7, -np.inf], [9 ,-np.inf, 10, np.nan]])>>> print(a)[[ 1. nan 3. 4.] [ inf 6. 7. -inf] [ 9. -inf 10. nan]]>>> loc_nan = np.isnan(a)>>> loc_inf = np.isinf(a)>>> a[loc_nan] = 111>>> a[loc_inf] = 222>>> print(a)[[ 1. 111. 3. 4.] [222. 6. 7. 222.] [ 9. 222. 10. 111.]] 【2x00】NumPy 索引【2x01】获取具体元素NumPy 的索引和 Python 列表的索引类似,可以通过中括号指定索引获取第 i 个值(从 0 开始计数): 12345678910>>> import numpy as np>>> a = np.array([1, 2, 3, 4])>>> print(a[0])1>>> print(a[2])3>>> print(a[-1])4>>> print(a[-2])3 在多维数组中,可以用逗号分隔的索引元组获取具体某个元素: 12345678910>>> import numpy as np>>> a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])>>> print(a)[[1 2 3] [4 5 6] [7 8 9]]>>> print(a[1, 2])6>>> print(a[0, -2])2 1234567891011121314>>> a = np.array([[[1,2,3,4], [5,6,7,8]], [[9,10,11,12], [13,14,15,16]], [[17,18,19,20], [21,22,23,24]]])>>> print(a)[[[ 1 2 3 4] [ 5 6 7 8]] [[ 9 10 11 12] [13 14 15 16]] [[17 18 19 20] [21 22 23 24]]]>>> print(a[0, 1, 2])7>>> print(a[1, 0, -1])12 【2x02】获取行或列1234567891011121314151617181920212223242526272829303132>>> a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]])>>> print(a)[[ 1 2 3] [ 4 5 6] [ 7 8 9] [10 11 12]]>>> print(a[1]) # 取一整行[4 5 6]>>> print(a[1, :]) # 取一整行,效果同上[4 5 6]>>> print(a[1, ...]) # 取一整行,效果同上[4 5 6]>>> print(a[:, 2]) # 取一整列[ 3 6 9 12]>>> print(a[..., 2]) # 取一整列,效果同上[ 3 6 9 12]>>> print(a[1:3]) # 取多行[[4 5 6] [7 8 9]]>>> print(a[:, 0:2]) # 取多列[[ 1 2] [ 4 5] [ 7 8] [10 11]]>>> print(a[[1, 3], :]) # 取第一、三行和所有列[[ 4 5 6] [10 11 12]]>>> print(a[:, [0, 2]]) # 取第零、二列和所有行[[ 1 3] [ 4 6] [ 7 9] [10 12]] 【2x03】布尔索引除了直接获取元素以外,还可以通过一个布尔数组来索引目标数组,即通过布尔运算(如:比较运算符)来获取符合指定条件的元素的数组。 以下实例将筛选出大于 6 的元素: 12345678>>> a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]])>>> print(a)[[ 1 2 3] [ 4 5 6] [ 7 8 9] [10 11 12]]>>> print(a[a > 6])[ 7 8 9 10 11 12] 以下实例使用取补运算符 ~ 来过滤掉 inf: 123456>>> import numpy as np>>> a = np.array([1, 2, np.inf, 3, -np.inf, 4, 5])>>> print(a)[ 1. 2. inf 3. -inf 4. 5.]>>> print(a[~np.isinf(a)])[1. 2. 3. 4. 5.] 【2x04】花式索引花式索引:传递一个索引数组来一次性获得多个数组元素,花式索引总是将数据复制到新数组中。 花式索引根据索引数组的值作为目标数组的某个轴的下标来取值。对于使用一维整型数组作为索引,如果目标是一维数组,那么索引的结果就是对应位置的元素;如果目标是二维数组,那么就是对应下标的行。 花式索引结果的形状与索引数组的形状一致,而不是与被索引数组的形状一致。 一维数组中的应用: 1234>>> import numpy as np>>> a = np.array([1, 2, 3, 4, 5, 6])>>> print([a[0], a[2], a[-1]])[1, 3, 6] 12345>>> import numpy as np>>> a = np.array([1, 2, 3, 4, 5, 6])>>> ind = [0, 2, -1]>>> print(a[ind])[1 3 6] 二维数组中的应用: 12345678>>> import numpy as np>>> a = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])>>> print(a)[[ 1 2 3 4] [ 5 6 7 8] [ 9 10 11 12]]>>> print([a[0, 1], a[1, 2], a[2, 3]])[2, 7, 12] 123456>>> import numpy as np>>> a = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])>>> row = np.array([0, 1, 2]) # 行>>> col = np.array([1, 2, 3]) # 列>>> print(a[row, col])[ 2 7 12] 【3x00】NumPy 切片Ndarray 数组对象基于 0 - n 的下标进行索引,与 Python 中列表的切片操作一样,NumPy 的切片也可以通过冒号分隔切片参数 [start:stop:step] 来进行切片操作,另外,NumPy 也提供了一个内置函数 slice(start, stop, step) 来进行切片操作。 slice 方法应用: 1234567>>> import numpy as np>>> a = np.arange(10)>>> print(a)[0 1 2 3 4 5 6 7 8 9]>>> b = slice(2, 8, 2) # 从索引 2 开始到索引 8 停止,步长为 2>>> print(a[b])[2 4 6] 通过冒号分隔切片参数 [start:stop:step] 来进行切片: 12345678>>> import numpy as np>>> a = np.arange(12)>>> print(a)[ 0 1 2 3 4 5 6 7 8 9 10 11]>>> print(a[1:9:2]) # 从索引 1 开始到索引 9 停止,步长为 2[1 3 5 7]>>> print(a[5:])[ 5 6 7 8 9 10 11] # 从索引 5 开始一直到最后一个元素 二数组中的切片,格式类似于 a[start:stop:step, start:stop:step],以逗号来分割行与列。 12345678910111213141516171819202122232425>>> import numpy as np>>> a = np.array([[1, 2, 3], [4, 5, 6], [7, 8 ,9], [10, 11, 12], [13, 14, 15], [16, 17, 18]])>>> print(a)[[ 1 2 3] [ 4 5 6] [ 7 8 9] [10 11 12] [13 14 15] [16 17 18]]>>> print(a[0:4:2]) # 按照行从索引 0 开始到索引 4 停止,步长为 2[[1 2 3] [7 8 9]]>>> print(a[0:4:2,...]) # 与上述方法相同,... 与 : 作用相同[[1 2 3] [7 8 9]]>>> print(a[...,1:]) # 按照列从索引 1 开始一直到最后一个元素[[ 2 3] [ 5 6] [ 8 9] [11 12] [14 15] [17 18]]>>> print(a[1:5:2,1:]) # 分别按照行从索引 1 开始到索引 5 停止,步长为 2,按照列从索引 1 开始一直到最后一个元素[[ 5 6] [11 12]] 三数组中的切片,格式类似于 a[start:stop:step, start:stop:step, start:stop:step],以逗号来分割块、行与列。 1234567891011121314151617181920>>> import numpy as np>>> a = np.array([[[1,2,3,4], [5,6,7,8]], [[9,10,11,12], [13,14,15,16]], [[17,18,19,20], [21,22,23,24]]])>>> print(a)[[[ 1 2 3 4] [ 5 6 7 8]] [[ 9 10 11 12] [13 14 15 16]] [[17 18 19 20] [21 22 23 24]]]>>> print(a[1,:,0:2]) # 取索引为 1 的块数、所有行、索引为 0 到 2 的列[[ 9 10] [13 14]]>>> print(a[1,:,:]) # 取索引为 1 的块数、所有行与列[[ 9 10 11 12] [13 14 15 16]]>>> print(a[1,...,:]) # ... 与 : 作用相同[[ 9 10 11 12] [13 14 15 16]] 【4x00】NumPy 数组运算以及广播原则NumPy 数组与数之间、数组与数组之间都支持加减乘除的计算。 对于数组与数之间的计算,由于 NumPy 的广播机制,加减乘除都会对数组的每一个元素进行操作。 对于数组与数组之间的计算,相同维度的,相同位置元素之间会进行计算,不同维度的,将自动触发广播机制。 广播(Broadcast)原则:如果两个数组的后缘维度(trailing dimension,即从末尾开始算起的维度)的轴长度相符,或其中的一方的长度为 1,则认为它们是广播兼容的。广播会在缺失和(或)长度为 1 的维度上进行。通俗理解,以下情况的两个数组均可进行广播:1、两个数组各维度大小从后往前比对均一致: 123456789101112131415161718192021>>> import numpy as np>>> a = np.ones((3, 4, 5))>>> b = np.ones((4, 5))>>> print((a+b).shape)(3, 4, 5)>>>>>> >>> c = np.ones((3))>>> d = np.ones((2, 3))>>> print((c+d).shape)(2, 3)>>>>>> >>> e = np.ones((3, 4, 5))>>> f = np.ones((4, 2))>>> print((e+f).shape)Traceback (most recent call last): File \"<pyshell#16>\", line 1, in <module> print((e+f).shape)ValueError: operands could not be broadcast together with shapes (3,4,5) (4,2) # 因为 e 和 f 维度大小此前往后对比不一致,所以会抛出异常 2、两个数组存在一些维度大小不相等时,在其中一个数组中,这个不相等的维度大小为 1: 123456>>> import numpy as np>>> a = np.ones((3, 4, 5))>>> b = np.ones((4, 1))>>> print((a+b).shape)(3, 4, 5)# 此时虽然 a 与 b 的维度大小此前往后对比不一致,但是 b 数组的这个维度大小为 1,所以仍然可以相加 那么当两个数组之间可以进行广播的时候,具体是怎样广播、怎样计算的呢?以下通过代码和图解来更进一步理解广播机制: 数组与数之间的运算: 123456789101112>>> import numpy as np>>> a = np.array([1, 2, 3, 4, 5])>>> print(a)[1 2 3 4 5]>>> print(a+1) # 对数组每一个元素都 +1[2 3 4 5 6]>>> print(a-1) # 对数组每一个元素都 -1[0 1 2 3 4]>>> print(a*2) # 对数组每一个元素都 *1[ 2 4 6 8 10]>>> print(a/2) # 对数组每一个元素都 /1[0.5 1. 1.5 2. 2.5] 相同维度的数组与数组之间的运算: 123456789101112131415161718192021>>> import numpy as np>>> a = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]])>>> b = np.array([[11, 12, 13, 14, 15], [16, 17, 18, 19, 20]])>>> print(a)[[ 1 2 3 4 5] [ 6 7 8 9 10]]>>> print(b)[[11 12 13 14 15] [16 17 18 19 20]]>>> print(a+b)[[12 14 16 18 20] [22 24 26 28 30]]>>> print(b-a)[[10 10 10 10 10] [10 10 10 10 10]]>>> print(a*b)[[ 11 24 39 56 75] [ 96 119 144 171 200]]>>> print(b/a)[[11. 6. 4.33333333 3.5 3. ] [ 2.66666667 2.42857143 2.25 2.11111111 2. ]] 不同维度的数组与数组之间的运算: 实例一:一个二维数组与一个一维数组相加,此时就会触发广播机制,代码与图解如下: 123456789101112131415>>> import numpy as np>>> a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]])>>> b = np.array([20, 20, 20])>>> print(a)[[ 1 2 3] [ 4 5 6] [ 7 8 9] [10 11 12]]>>> print(b)[20 20 20]>>> print(a+b)[[21 22 23] [24 25 26] [27 28 29] [30 31 32]] 实例二:一个 4 行 3 列的二维数组与一个 4 行 1 列的二维数组相加,此时就会触发广播机制,代码与图解如下: 123456789101112131415161718>>> import numpy as np>>> a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]])>>> b = np.array([[1], [2], [3], [4]])>>> print(a)[[ 1 2 3] [ 4 5 6] [ 7 8 9] [10 11 12]]>>> print(b)[[1] [2] [3] [4]]>>> print(a+b)[[ 2 3 4] [ 6 7 8] [10 11 12] [14 15 16]] 实例三:一个 3 块 4 行 2 列,即 shape=(3, 4, 2) 的三维数组与一个 1 块 4 行 2 列,即 shape=(1, 4, 2) 的三维数组相加,此时就会触发广播机制,代码与图解如下: 123456789101112131415161718192021222324252627282930313233343536373839404142>>> import numpy as np>>> a = np.array([[[1, 2], [3, 4], [5, 6], [7, 8]], [[9, 10], [11, 12], [13, 14], [15, 16]], [[17, 18], [19, 20], [21, 22], [23, 24]]])>>> b = np.array([[[1, 2], [3, 4], [5, 6], [7, 8]]])>>> print(a)[[[ 1 2] [ 3 4] [ 5 6] [ 7 8]] [[ 9 10] [11 12] [13 14] [15 16]] [[17 18] [19 20] [21 22] [23 24]]]>>> print(b)[[[1 2] [3 4] [5 6] [7 8]]]>>> print(a.shape)(3, 4, 2)>>> print(b.shape)(1, 4, 2)>>> print(a+b)[[[ 2 4] [ 6 8] [10 12] [14 16]] [[10 12] [14 16] [18 20] [22 24]] [[18 20] [22 24] [26 28] [30 32]]] 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/104988137未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【5x00】数组的拼接与元素的添加【5x01】将数组转换成列表,拼接完成再转换成数组数组的拼接,可以先将数组转成列表,利用列表的拼接函数,如:append()、extend() 等进行拼接处理,然后再将列表转成数组即可。 1234567891011121314>>> import numpy as np>>> a = np.array([1, 2, 3])>>> b = np.array([4, 5, 6])>>> a_list = list(a)>>> b_list = list(b)>>> for i in b_list: a_list.append(i) >>> a_list[1, 2, 3, 4, 5, 6]>>> a = np.array(a_list)>>> aarray([1, 2, 3, 4, 5, 6]) 【5x02】numpy.append()numpy.append() 方法可以将一个数组附加到另一个数组的尾部,与 Python 列表中的 append 方法类似,仅支持两个数组之间的拼接,不能一次性拼接多个数组。 基本语法:numpy.append(a, values, axis=None) 参数解释: 参数 描述 a 被添加元素的目标数组 values 要添加元素的目标数组,即将该数组添加到 a 数组的尾部如果指定了 axis 的值,则要求该数组的维度必须与 a 相同 axis 指定轴,按照指定轴的方向进行拼接如果未指定轴,则在使用前会将 a 和 values 都展平 应用举例: 1234567891011121314151617>>> import numpy as np>>> a = np.array([1, 2, 3])>>> b = np.array([4, 5, 6])>>> print(np.append(a, b)) # 两个一维数组进行拼接[1 2 3 4 5 6]>>> >>> a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])>>> b = np.array([10, 11, 12])>>> print(np.append(a, b)) # 一维数组与二维数组进行拼接[ 1 2 3 4 5 6 7 8 9 10 11 12]>>> >>> a = np.array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]])>>> b = np.array([[13, 14, 15], [16, 17, 18]])>>> print(np.append(a, b)) # 二维数组与三维数组进行拼接[ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18]>>> print(type(np.append(a, b))) # 拼接后的数组仍然是一个 ndarray 对象<class 'numpy.ndarray'> 指定 axis 的值举例(指定了 axis 的值,要求两个数组的维度必须相同): 123456789101112131415>>> import numpy as np>>> a = np.array([[1, 2, 3], [4, 5, 6]])>>> b = np.array([[7, 8, 9]])>>> print(np.append(a, b, axis=0)) # a、b 均为二维数组,指定 axis 值为 0[[1 2 3] [4 5 6] [7 8 9]]>>>>>> a = np.array([[1, 2, 3], [4, 5, 6]])>>> b = np.array([7, 8, 9])>>> print(np.append(a, b, axis=0)) # a 为二维数组,b 为一维数组,指定 axis 值为 0,此时会报错Traceback (most recent call last): ... ...ValueError: all the input arrays must have same number of dimensions, ... 【5x03】numpy.concatenate()numpy.concatenate() 方法能够一次完成多个相同形状数组的拼接。该方法效率更高,适合大规模的数据拼接。 基本语法:numpy.concatenate((a1, a2, ...), axis=0) 参数解释: 参数 描述 a1, a2, … 要拼接的多个数组,要求各数组形状相同 axis 指定轴,按照指定轴的方向进行拼接,默认为 0 轴 要求各数组形状相同举例:如果 axis=0,则要求 1 轴(竖轴)相同,如果 axis=1,则要求 0 轴(横轴)相同。应用举例:1234567891011>>> import numpy as np>>> a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])>>> b = np.array([[10, 11, 12], [13, 14, 15]])>>> c = np.array([[16, 17, 18]]) # 三个二维数组的 1 轴(竖轴)相同>>> print(np.concatenate((a, b, c))) # 三个二维数组默认沿 0 轴(横轴)进行拼接[[ 1 2 3] [ 4 5 6] [ 7 8 9] [10 11 12] [13 14 15] [16 17 18]]1234567>>> import numpy as np>>> a = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])>>> b = np.array([[9, 10, 11], [12, 13, 14]])>>> c = np.array([[15, 16], [17, 18]]) # 三个二维数组的 0 轴(横轴)相同>>> print(np.concatenate((a, b, c), axis=1)) # 三个二维数组沿 1 轴(竖轴)进行拼接[[ 1 2 3 4 9 10 11 15 16] [ 5 6 7 8 12 13 14 17 18]]123456789>>> import numpy as np>>> a = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])>>> b = np.array([[9, 10, 11], [12, 13, 14]])>>> c = np.array([[15, 16], [17, 18]]) # 三个二维数组的 0 轴(横轴)相同>>> print(np.concatenate((a, b, c), axis=0)) # 三个二维数组沿 0 轴(横轴)进行拼接将会报错Traceback (most recent call last): ... ...ValueError: all the input array dimensions for the concatenation axis must match exactly, ...### 【5x04】numpy.stack()numpy.stack() 方法用于沿新轴连接数组序列。基本语法:numpy.stack(arrays, axis=0)参数解释:| 参数 | 描述 || —— | —— || arrays | 相同形状的数组序列 || axis | 返回数组中的轴,输入数组将沿着该轴来进行堆叠 |应用举例:12345678910>>> import numpy as np>>> a = np.array([1, 2, 3])>>> b = np.array([4, 5, 6])>>> print(np.stack((a, b),axis=0))[[1 2 3] [4 5 6]]>>> print(np.stack((a, b),axis=1))[[1 4] [2 5] [3 6]]123456789101112131415>>> import numpy as np>>> a = np.array([[1,2],[3,4]])>>> b = np.array([[5,6],[7,8]])>>> print(np.stack((a,b),axis=0))[[[1 2] [3 4]] [[5 6] [7 8]]]>>> print(np.stack((a,b),axis=1))[[[1 2] [5 6]] [[3 4] [7 8]]]### 【5x05】numpy.vstack() numpy.vstack() 方法通过垂直堆叠来生成数组。 基本语法:numpy.vstack(tup) 参数解释:tup:数组序列,如果是一维数组进行堆叠,则数组长度必须相同,其它数组除了第一个轴(axis=0)的长度可以不同外,其它轴的长度必须相同。 应用举例: 12345678910111213>>> import numpy as np>>> a = np.array([1, 2, 3])>>> b = np.array([4, 5, 6])>>> print(np.vstack((a,b)))[[1 2 3] [4 5 6]]>>> >>> b = np.array([4, 5])>>> print(np.vstack((a,b))) # 一维数组长度不一样时将抛出异常Traceback (most recent call last): ... ...ValueError: all the input array dimensions for the concatenation axis must match exactly, ... 123456789101112>>> import numpy as np>>> a = np.array([[1,2],[3,4]])>>> b = np.array([[5,6],[7,8]])>>> a.shape(2, 2)>>> b.shape(2, 2)>>> print(np.vstack((a,b)))[[1 2] [3 4] [5 6] [7 8]] 12345678910111213>>> import numpy as np>>> a = np.array([[1,2],[3,4]])>>> b = np.array([[5,6],[7,8],[9,10]])>>> a.shape(2, 2)>>> b.shape(3, 2)>>> print(np.vstack((a,b))) # 第一个轴(axis=0)可以不同,其它轴必须相同[[ 1 2] [ 3 4] [ 5 6] [ 7 8] [ 9 10]] 【5x06】numpy.hstack()numpy.hstack() 方法通过水平堆叠来生成数组。 基本语法:numpy.hstack(tup) 参数解释:tup:数组序列,除了一维数组的堆叠可以是不同长度外,其它数组堆叠时,除了第二个轴(axis=1)的长度可以不同外,其它轴的长度必须相同。 应用举例: 12345>>> import numpy as np>>> a = np.array([1, 2, 3])>>> b = np.array([4, 5])>>> print(np.hstack((a,b)))[1 2 3 4 5] 12345678910>>> import numpy as np>>> a = np.array([[1,2],[3,4]])>>> b = np.array([[5,6],[7,8]])>>> a.shape(2, 2)>>> b.shape(2, 2)>>> print(np.hstack((a,b)))[[1 2 5 6] [3 4 7 8]] 12345678910>>> import numpy as np>>> a = np.array([[1,2],[3,4]])>>> b = np.array([[5,6,7],[8,9,10]])>>> a.shape(2, 2)>>> b.shape(2, 3)>>> print(np.hstack((a,b))) # 第二个轴(axis=1)可以不同,其它轴必须相同[[ 1 2 5 6 7] [ 3 4 8 9 10]] 【5x07】numpy.dstack()numpy.dstack() 方法会沿着第三个维度拼接数组。 基本语法:numpy.dstack(tup) 参数解释:tup:数组序列,除了第三个轴(axis=2)的长度可以不同外,其它轴的长度必须相同。一维或二维数组必须具有相同的形状。 应用举例: 12345678910111213>>> import numpy as np>>> a = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])>>> b = np.array([[[9], [10]], [[11], [12]]])>>> a.shape(2, 2, 2)>>> b.shape(2, 2, 1)>>> print(np.dstack((a,b))) # a 与 b 的第三个轴(axis=2)一个是 2,另一个是 1,可以不同,但其他轴必须相同[[[ 1 2 9] [ 3 4 10]] [[ 5 6 11] [ 7 8 12]]] 123456789101112131415161718>>> import numpy as np>>> a = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])>>> b = np.array([[[9, 10], [11, 12]]])>>> a.shape(2, 2, 2)>>> b.shape(1, 2, 2)>>> print(np.dstack((a,b))) # a 与 b 的第一个轴(axis=0)不同,将会抛出异常[[[ 1 2 9] [ 3 4 10]] [[ 5 6 11] [ 7 8 12]]]>>> print(np.dstack((a,b)))Traceback (most recent call last): ... ...ValueError: all the input array dimensions for the concatenation axis must match exactly, ... 【5x08】以上几种方法的区别 concatenate() 方法在 axis=0 的时候相当于 vstack() 方法; concatenate() 方法在 axis=1 的时候相当于 hstack() 方法; concatenate() 方法在 axis=2 的时候相当于 dstack() 方法; concatenate() 方法不会生成一个新的维度,且数组的维度不一定相同,而 stack() 方法会生成一个新的维度,并且要求所有数组形状都要一样。 concatenate() 方法与 stack() 方法比较: 123456789101112131415161718>>> import numpy as np>>> a = np.array([1, 2, 3])>>> b = np.array([4, 5, 6])>>> c = np.stack((a, b), axis=0)>>> d = np.concatenate((a, b), axis=0)>>> a.shape(3,)>>> b.shape(3,)>>> c.shape(2, 3)>>> d.shape(6,)>>> print(c)[[1 2 3] [4 5 6]]>>> print(d)[1 2 3 4 5 6] 【5x09】numpy.insert()numpy.insert() 方法沿指定轴在指定索引之前插入值。 基本语法:numpy.insert(arr, obj, values, axis=None) 参数解释: 参数 描述 arr 原数组 obj 索引值,将在其之前插入值 values 要插入的值 axis 轴,将沿着该轴进行插入操作,如果未指定,则插入前,原数组会被展开,变为一维数组 应用举例: 12345678910111213>>> import numpy as np>>> a = np.array([[1,2],[3,4],[5,6]])>>> print(np.insert(a, 3, [0, 0])) # 未指定 axis 参数,在插入之前原数组会被展开[1 2 3 0 0 4 5 6]>>> print(np.insert(a, 1, [0], axis = 0)) # 沿 0 轴广播插入[[1 2] [0 0] [3 4] [5 6]]>>> print(np.insert(a, 1, 11, axis = 1)) # 沿 1 轴广播插入[[ 1 11 2] [ 3 11 4] [ 5 11 6]] 【5x10】numpy.r_numpy.r_:r 为 row(行) 的缩写,即按照行连接两个矩阵,要求列数相等。 应用举例: 123456789>>> import numpy as np>>> a = np.array([[1, 2, 3],[7, 8, 9]])>>> b = np.array([[4, 5, 6],[1, 2, 3]])>>> >>> np.r_[a, b]array([[1, 2, 3], [7, 8, 9], [4, 5, 6], [1, 2, 3]]) 【5x11】numpy.c_numpy.c_:c 为 column(列) 的缩写,即按照列连接两个矩阵,要求行数相等。 应用举例: 1234567>>> import numpy as np>>> a = np.array([[1, 2, 3],[7, 8, 9]])>>> b = np.array([[4, 5, 6],[1, 2, 3]])>>> >>> np.c_[a, b]array([[1, 2, 3, 4, 5, 6], [7, 8, 9, 1, 2, 3]]) 【6x00】数组的分割与元素的删除【6x01】numpy.split()numpy.split() 方法可以沿特定的轴将数组均等的分割为子数组。如果不能等分,将抛出异常。 基本语法:numpy.split(ary, indices_or_sections, axis=0) 参数解释: 参数 描述 ary 被分割的数组 indices_or_sections 如果是一个整数 N,则该数组将沿 axis 均分为 N 个数组如果是一维整数数组,则数组元素代表每个分割点位置(左闭右开),N 个分裂点会得到 N + 1 个子数组 axis 沿着哪个维度进行分割,默认为 0,横向分割;为 1 时,纵向分割 应用举例: 12345678910111213141516171819202122232425262728293031323334>>> import numpy as np>>> a = np.arange(10)>>> print(a)[0 1 2 3 4 5 6 7 8 9]>>> print(np.split(a, 5)) # 将数组分为五个大小相等的子数组[array([0, 1]), array([2, 3]), array([4, 5]), array([6, 7]), array([8, 9])]>>>>>> print(np.split(a, 4)) # 无法等分的情况下将抛出异常Traceback (most recent call last): ... ...ValueError: array split does not result in an equal division>>>>>> print(np.split(a, [4, 8])) # 分割点为索引 4 和 8 的位置,相当于 a[:4]、a[4:8]、a[8:][array([0, 1, 2, 3]), array([4, 5, 6, 7]), array([8, 9])]>>>>>> a = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]])>>> print(np.split(a, 5, axis=1)) # 指定 axis=1,按照纵向分割数组[array([[1], [6]]), array([[2], [7]]), array([[3], [8]]), array([[4], [9]]), array([[ 5], [10]])] 【6x02】numpy.array_split()numpy.array_split() 的用法和作用都与 split() 方法一致,都可以用一个整数或者整数列表来分割数组。 两者的区别:如果输入的是一个 int 类型的数字,那么在 split() 方法中,数组必须是均等的分割,否则就会报错,而在 array_split() 方法中是可以进行不均等的分割的。 基本语法:numpy.array_split(ary, indices_or_sections, axis=0) 参数解释: 参数 描述 ary 被分割的数组 indices_or_sections 如果是一个整数 N,则该数组将沿 axis 分割为 N 个数组,可以不是均分的如果是一维整数数组,则数组元素代表每个分割点位置(左闭右开),N 个分裂点会得到 N + 1 个子数组 axis 沿着哪个维度进行分割,默认为 0,横向分割;为 1 时,纵向分割 应用举例: 1234567>>> import numpy as np>>> a = np.array([1, 2, 3, 4, 5, 6, 7])>>> print(np.array_split(a, 3))[array([1, 2, 3]), array([4, 5]), array([6, 7])] # 可以是不均分的>>>>>> print(np.array_split(a, 4))[array([1, 2]), array([3, 4]), array([5, 6]), array([7])] 【6x03】numpy.vsplit()numpy.vsplit() 方法相当于 split() 方法在 axis=0 时的效果,即横向分割数组。 基本语法:numpy.vsplit(ary, indices_or_sections) 参数解释: 参数 描述 ary 被分割的数组 indices_or_sections 如果是一个整数 N,则该数组将沿 axis 分为 N 个相等的数组如果是一维整数数组,则数组元素代表每个分割点位置(左闭右开),N 个分裂点会得到 N + 1 个子数组 应用举例: 1234567891011121314>>> import numpy as np>>> a = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]])>>> a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]])>>> print(np.vsplit(a, 2))[array([[1, 2, 3], [4, 5, 6]]), array([[ 7, 8, 9], [10, 11, 12]])]>>>>>> print(np.vsplit(a, [1, 3]))[array([[1, 2, 3]]), array([[4, 5, 6], [7, 8, 9]]), array([[10, 11, 12]])] 【6x04】numpy.hsplit()numpy.hsplit() 方法相当于 split() 方法在 axis=1 时的效果,即纵向分割数组。 基本语法:numpy.hsplit(ary, indices_or_sections) 参数解释: 参数 描述 ary 被分割的数组 indices_or_sections 如果是一个整数 N,则该数组将沿 axis 分为 N 个相等的数组如果是一维整数数组,则数组元素代表每个分割点位置(左闭右开),N 个分裂点会得到 N + 1 个子数组 应用举例: 123456789101112131415161718192021>>> import numpy as np>>> a = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]])>>> print(np.hsplit(a, 5))[array([[1], [6]]), array([[2], [7]]), array([[3], [8]]), array([[4], [9]]), array([[ 5], [10]])]>>>>>> print(np.hsplit(a, [1, 3]))[array([[1], [6]]), array([[2, 3], [7, 8]]), array([[ 4, 5], [ 9, 10]])] 【6x05】numpy.dsplit()numpy.dsplit() 方法相当于 split() 方法在 axis=2 时的效果,即沿第三轴将数组拆分为多个子数组。 基本语法:numpy.dsplit(ary, indices_or_sections) 参数解释: 参数 描述 ary 被分割的数组 indices_or_sections 如果是一个整数 N,则该数组将沿 axis 分为 N 个相等的数组如果是一维整数数组,则数组元素代表每个分割点位置(左闭右开),N 个分裂点会得到 N + 1 个子数组 应用举例: 12345678910111213141516171819202122232425262728>>> import numpy as np>>> a = np.arange(16).reshape(2, 2, 4)>>> print(a)[[[ 0 1 2 3] [ 4 5 6 7]] [[ 8 9 10 11] [12 13 14 15]]]>>> print(np.dsplit(a, 2))[array([[[ 0, 1], [ 4, 5]], [[ 8, 9], [12, 13]]]), array([[[ 2, 3], [ 6, 7]], [[10, 11], [14, 15]]])]>>>>>> print(np.dsplit(a, [3, 6]))[array([[[ 0, 1, 2], [ 4, 5, 6]], [[ 8, 9, 10], [12, 13, 14]]]), array([[[ 3], [ 7]], [[11], [15]]]), array([], shape=(2, 2, 0), dtype=int32)] 【6x06】numpy.delete()numpy.delete() 方法返回一个从原数组中删除了指定子数组的新数组。 基本语法:numpy.delete(arr,obj,axis=None) 参数解释: 参数 描述 arr 原数组 obj 可以是切片、整数或整数数组形式,表示沿指定轴删除的子数组的索引当 obj 为切片形式时,要用 np.s_[:] 的格式 axis 轴,将沿着该轴进行插入操作,如果未指定,则插入前,原数组会被展开,变为一维数组 应用举例: 1234567891011121314151617181920212223>>> import numpy as np>>> a = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])>>> print(np.delete(a, 8)) # 未指定 axis 参数,在插入之前原数组会被展开,然后再删除索引为 8 的元素[ 1 2 3 4 5 6 7 8 10 11 12]>>>>>> print(np.delete(a, 1, axis=0)) # 指定 axis=0,删除索引值为 1 即第二行[[ 1 2 3 4] [ 9 10 11 12]]>>>>>> print(np.delete(a, 1, axis=1)) # 指定 axis=1,删除索引值为 1 即第二列[[ 1 3 4] [ 5 7 8] [ 9 11 12]]>>>>>> print(np.delete(a, np.s_[:2], axis=1)) # 切片形式,删除前两列[[ 3 4] [ 7 8] [11 12]]>>>>>> print(np.delete(a, [0, 2], axis=1)) # 数组形式,删除索引值为 0 和 2 的列[[ 2 4] [ 6 8] [10 12]] 【6x07】numpy.unique()numpy.unique() 方法用于去除数组中的重复元素。 基本语法:numpy.unique(arr, return_index=False, return_inverse=False, return_counts=False, axis=None) 参数解释: 参数 描述 arr 原数组,如果不是一维数组则会被展开为一维数组 return_index 如果为 true,则返回新列表元素在旧列表中的位置(下标),并以列表形式储 return_inverse 如果为 true,则返回旧列表元素在新列表中的位置(下标),并以列表形式储 return_counts 如果为 true,则返回去重数组中的元素在原数组中的出现次数 axis 指定轴 应用举例: 未指定 axis 值,原数组将会被展开: 1234567>>> import numpy as np>>> a = np.array([1, 1, 2, 2, 3, 4, 5, 5])>>> b = np.array([[1, 1], [2, 3], [3, 4]])>>> print(np.unique(a))[1 2 3 4 5]>>> print(np.unique(b))[1 2 3 4] 指定 axis 值: 12345678910111213141516>>> import numpy as np>>> a = np.array([[1, 0, 1], [1, 0, 1], [2, 3, 2], [5, 6, 5]])>>> print(a)[[1 0 1] [1 0 1] [2 3 2] [5 6 5]]>>> print(np.unique(a, axis=0)) # 删除相同的行[[1 0 1] [2 3 2] [5 6 5]]>>> print(np.unique(a, axis=1)) # 删除相同的列[[0 1] [0 1] [3 2] [6 5]] return_counts 为 True 时,返回去重数组中的元素在原数组中的出现次数: 12345>>> import numpy as np>>> a = np.array([1, 1, 1, 2, 2, 3, 4, 5, 5])>>> print(np.unique(a, return_counts=True))(array([1, 2, 3, 4, 5]), array([3, 2, 1, 1, 2], dtype=int64))# 前一个 array 表示去重后的数组,后一个 array 表示每一个元素在原数组中出现的次数 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/104988137未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"NumPy","slug":"Python-数据分析/NumPy","permalink":"https://www.itrhx.com/categories/Python-数据分析/NumPy/"}],"tags":[{"name":"NumPy","slug":"NumPy","permalink":"https://www.itrhx.com/tags/NumPy/"},{"name":"数组","slug":"数组","permalink":"https://www.itrhx.com/tags/数组/"},{"name":"索引","slug":"索引","permalink":"https://www.itrhx.com/tags/索引/"},{"name":"切片","slug":"切片","permalink":"https://www.itrhx.com/tags/切片/"},{"name":"广播","slug":"广播","permalink":"https://www.itrhx.com/tags/广播/"},{"name":"拼接","slug":"拼接","permalink":"https://www.itrhx.com/tags/拼接/"},{"name":"分割","slug":"分割","permalink":"https://www.itrhx.com/tags/分割/"}]},{"title":"Python 数据分析三剑客之 NumPy(一):理解 NumPy / 数组基础","slug":"A62-NumPy-01","date":"2020-03-20T05:15:50.677Z","updated":"2020-08-06T03:01:29.975Z","comments":true,"path":"2020/03/20/A62-NumPy-01/","link":"","permalink":"https://www.itrhx.com/2020/03/20/A62-NumPy-01/","excerpt":"","text":"NumPy 系列文章: Python 数据分析三剑客之 NumPy(一):理解 NumPy / 数组基础 Python 数据分析三剑客之 NumPy(二):数组索引 / 切片 / 广播 / 拼接 / 分割 Python 数据分析三剑客之 NumPy(三):数组的迭代与位运算 Python 数据分析三剑客之 NumPy(四):字符串函数总结与对比 Python 数据分析三剑客之 NumPy(五):数学 / 算术 / 统计 / 排序 / 条件 / 判断函数合集 Python 数据分析三剑客之 NumPy(六):矩阵 / 线性代数库与 IO 操作 专栏: 【NumPy 专栏】【Pandas 专栏】【Matplotlib 专栏】 推荐学习资料与网站: 【NumPy 中文网】【Pandas 中文网】【Matplotlib 中文网】【NumPy、Matplotlib、Pandas 速查表】 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/104870084未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【1x00】了解 NumPyNumPy 是使用 Python 进行科学计算的基础包,支持大量的维度数组与矩阵运算,对数组运算提供大量的数学函数库。NumPy 重在数值计算,是大部分 Python 科学计算库的基础库,多用于在大型、多维数组上执行数值运算。 NumPy 主要包含如下的内容: 一个强大的 N 维数组对象(Ndarray); 复杂的广播功能函数; 集成 C/C++/Fortran 代码的工具; 具有线性代数、傅里叶变换、随机数生成等功能。 【2x00】NumPy 数组与 Python 列表的区别Numpy 使用 Ndarray 对象来处理多维数组,Python 列表通常存储一维数组,通过列表的嵌套也可以实现多维数组。 Numpy 的 Ndarray 对象是一个快速而灵活的大数据容器。Numpy 专门针对数组的操作和运算进行了设计,所以数组的存储效率和输入输出性能远优于 Python 中的嵌套列表,数组越大,Numpy 的优势就越明显。 通常 Numpy 数组中的所有元素的类型都是相同的,而 Python 列表中的元素类型是任意的,所以在通用性能方面 Numpy 数组不及 Python 列表,但在科学计算中,可以省掉很多循环语句,代码使用方面比 Python 列表简单的多。 Python 列表的元素不同类型举例: 123>>> l = [True, '2', 3.2, 5]>>> [type(item) for item in l][<class 'bool'>, <class 'str'>, <class 'float'>, <class 'int'>] Python 列表中的每一项必须包含各自的类型信息、引用计数和其他信息,也就是说,每一项都是一个完整的 Python 对象,同时,Python 列表还包含一个指向指针块的指针,其中的每一个指针对应一个完整的 Python 对象,另外,列表的优势是灵活,因为每个列表元素是一个包含数据和类型信息的完整结构体。相反 NumPy 数组缺乏这种灵活性,但是 NumPy 却能更有效地存储和操作数据。 【3x00】理解 NumPy Ndarray 对象NumPy 提供了一个 N 维数组类型,即 Ndarray,它是一系列同类型数据的集合,是用于存放同类型元素的多维数组,以 0 下标为开始进行集合中元素的索引,所有 Ndarray 中的每个元素在内存中都有相同存储大小的区域。 Ndarray 内部由以下内容组成: 一个指向数据(内存或内存映射文件中的一块数据)的指针; 数据类型或 dtype,描述在数组中的固定大小值的格子; 一个表示数组形状(shape)的元组,表示各维度大小的元组; 一个跨度元组(stride),其中的整数指的是为了前进到当前维度下一个元素需要“跨过”的字节数。 【4x00】理解不同维度的数组NumPy 数组的维数称为秩(rank),秩就是轴的数量,即数组的维度,一维数组的秩为 1,二维数组的秩为 2,以此类推。 在 NumPy 中,每一个线性的数组称为是一个轴(axis),也就是维度(dimensions)。比如说,二维数组相当于是两个一维数组,其中第一个一维数组中每个元素又是一个一维数组。所以一维数组就是 NumPy 中的轴(axis),第一个轴相当于是底层数组,第二个轴是底层数组里的数组。而轴的数量 — 秩,就是数组的维数。 很多时候可以声明 axis,axis=0,表示沿着第 0 轴进行操作,即对每一列进行操作;axis=1,表示沿着第 1 轴进行操作,即对每一行进行操作。 一维数组: 123456>>> import numpy as np>>> a = np.array([1, 2, 3, 4])>>> print(a)[1 2 3 4]>>> print(a.shape)(4,) 二维数组: 12345678>>> import numpy as np>>> a = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])>>> print(a)[[1 2 3 4] [5 6 7 8]]>>> print(a.shape)(2, 4) a.shape 输出数组的维度,对于此二维数组,可以理解为 2 行 4 列。 三维数组: 1234567891011121314>>> import numpy as np>>> a = np.array([[[1,2,3,4], [5,6,7,8]], [[9,10,11,12], [13,14,15,16]], [[17,18,19,20], [21,22,23,24]]])>>> print(a)[[[ 1 2 3 4] [ 5 6 7 8]] [[ 9 10 11 12] [13 14 15 16]] [[17 18 19 20] [21 22 23 24]]]>>> print(a.shape)(3, 2, 4) a.shape 输出数组的维度,对于此三维数组,可以理解为 3 块,每块有 2 行 4 列。 有网友对三维数组的这个图有疑问,认为横线应该是 axis=0,竖线是 axis=1,斜线是 axis=2,这个确实有点儿绕,不要受到前面一维二维的影响,我把我的理解又画了一张图出来,另外大家可以尝试去取三维数组里面的某个值,多想一下就可以理解了。欢迎各位大佬一起交流学习! 【5x00】创建 Ndarray 对象(创建数组)【5x01】一张表快速了解创建数组的不同方法 方法 描述 numpy.array() 将输入数据(列表、元组、Ndarray 等)转换为数组形式当数据源为 Ndarray 时,该方法仍然会 copy 出一个副本,占用新的内存 numpy.asarray() 将输入数据(列表、元组、Ndarray 等)转换为数组形式当数据源为 Ndarray 时,该方法不会 copy 出一个副本,不占用新的内存 numpy.arange() 创建一个一维数组,该数组由一个等差数列构成通过指定开始值、终值和步长创建等差数列,得到的结果数组不包含终值 numpy.linspace() 创建一个一维数组,该数组由一个等差数列构成通过指定开始值、终值和元素个数创建等差数列,可通过 endpoint 参数指定是否包含终值 numpy.logspace() 创建一个一维数组,该数组由一个等比数列构成 numpy.empty() 创建一个指定形状、数据类型且未初始化的数组 numpy.zeros() 创建一个指定大小的数组,数组元素以 0 来填充 numpy.ones() 创建一个指定大小的数组,数组元素以 1 来填充 numpy.eye() 创建一个对角矩阵数组,返回一个二维数组,对角线上值为 1,其余位置为 0 numpy.frombuffer() 将缓冲区解释为一维数组,接受 buffer 输入参数,以流的形式读入并转化成 Ndarray 对象 numpy.fromiter() 从可迭代对象中建立 Ndarray 对象,返回一个一维数组 【5x02】numpy.array()调用 NumPy 的 array 方法即可创建一个 Ndarray 对象,即创建一个数组。 基本语法:numpy.array(object, dtype = None, copy = True, order = None, subok = False, ndmin = 0) 参数解释: 参数 描述 object 数组或嵌套的数列 dtype 数组元素的数据类型,可选 copy 对象是否需要复制,可选 order 创建数组的样式,C为行方向,F为列方向,A为任意方向(默认) subok 默认返回一个与基类类型一致的数组 ndmin 指定生成数组的最小维度 创建一个一维数组: 123456>>> import numpy as np>>> a = np.array([1, 2, 3])>>> print(a)[1 2 3]>>> print(type(a))<class 'numpy.ndarray'> 创建一个二维数组: 1234567>>> import numpy as np>>> a = np.array([[1, 2, 3], [4, 5, 6]])>>> print(a)[[1 2 3] [4 5 6]]>>> print(type(a))<class 'numpy.ndarray'> 创建一个三维数组: 12345678>>> import numpy as np>>> a = np.array([[[1,2,3], [4,5,6]], [[7,8,9], [10,11,12]]])>>> print(a)[[[ 1 2 3] [ 4 5 6]] [[ 7 8 9] [10 11 12]]] 【5x03】numpy.asarray()numpy.asarray() 方法将输入数据(列表、元组、Ndarray 等)转换为数组形式,与 numpy.array() 方法类似,但 asarray 参数比 array 少两个,另外最大的区别是当数据源为 Ndarray 时,array 方法仍然会 copy 出一个副本,占用新的内存,但 asarray 方法不会。 基本语法:numpy.asarray(a, dtype=None, order=None) 参数解释: 参数 描述 a 待转换对象,可以是列表,元组,列表元组,元组列表,多维数组等 dtype 可选项,指定数据类型 order 可选项,以行优先(C)或列优先(F)的顺序存储多维数据在内存中 将列表转换为 Ndarray: 1234567>>> import numpy as np>>> l = [1,2,3,4]>>> n = np.asarray(l)>>> print(n)[1 2 3 4]>>> print(type(n))<class 'numpy.ndarray'> 将元组转换为 Ndarray: 1234567>>> import numpy as np>>> l = (1,2,3,4)>>> n = np.asarray(l)>>> print(n)[1 2 3 4]>>> print(type(n))<class 'numpy.ndarray'> 将元组列表转换为 Ndarray: 1234567>>> import numpy as np>>> l = [(1,2,3),(4,5)]>>> n = np.asarray(l)>>> print(n)[(1, 2, 3) (4, 5)]>>> print(type(n))<class 'numpy.ndarray'> 指定 dtype 参数: 1234567>>> import numpy as np>>> l = [1,2,3]>>> n = np.asarray(l, dtype=float)>>> print(n)[1. 2. 3.]>>> print(type(n))<class 'numpy.ndarray'> numpy.asarray() 方法和 numpy.array() 的区别演示: 当输入数据为列表、元组等格式时,两者没有区别,都可以将其转为数组格式: 1234567891011121314151617>>> import numpy as np>>> a = [[1,2,3], [4,5,6], [7,8,9]]>>> b = np.array(a)>>> c = np.asarray(a)>>> a[1] = 0>>> print(a)[[1, 2, 3], 0, [7, 8, 9]]>>> print(type(a)) # a 为列表<class 'list'>>>> print(b) # 列表对象 a 的值改变,array 方法得到的值不会改变[[1 2 3] [4 5 6] [7 8 9]]>>> print(c) # 列表对象 a 的值改变,asarray 方法得到的值不会改变[[1 2 3] [4 5 6] [7 8 9]] 当输入数据为 Ndarray 时,array 方法仍然会 copy 出一个副本,占用新的内存,但 asarray 方法不会: 12345678910111213141516171819>>> import numpy as np>>> a = np.ones((3,3))>>> b = np.array(a)>>> c = np.asarray(a)>>> a[1][1] = 2>>> print(a)[[1. 1. 1.] [1. 2. 1.] [1. 1. 1.]]>>> print(type(a)) # a 为 Ndarray 对象<class 'numpy.ndarray'>>>> print(b) # Ndarray 对象 a 的值改变,array 方法得到的值不会改变[[1. 1. 1.] [1. 1. 1.] [1. 1. 1.]]>>> print(c) # Ndarray 对象 a 的值改变,asarray 方法得到的值也将改变[[1. 1. 1.] [1. 2. 1.] [1. 1. 1.]] 【5x04】numpy.arange()numpy.arange() 方法用于创建一个一维数组,在指定的间隔内返回均匀间隔的数字并组成数组(Ndarray 对象),即该数组是一个等差数列构成的。arange() 类似 Python 的 range(),但是 arange() 的步长可以为小数,而 range() 的步长只能是整数。 基本语法:numpy.arange([start, ]stop, [step, ]dtype=None) 参数解释: 参数 描述 start 起始值,数字,可选项,默认起始值为 0,生成的元素包括起始值 stop 结束值,数字,生成的元素不包括结束值 step 步长,数字,可选项, 默认步长为 1,如果指定了 step,则必须给出 start dtype 输出数组的类型,如果未给出 dtype,则从其他输入参数推断数据类型 应用举例: 12345678>>> import numpy as np>>> a = np.arange(5) # 相当于 np.array([0, 1, 2, 3, 4])>>> b = np.arange(2, 5) # 相当于 np.array([2, 3, 4])>>> c = np.arange(2, 9, 3)>>> print('a = %s\\nb = %s\\nc = %s' %(a,b,c))a = [0 1 2 3 4]b = [2 3 4]c = [2 5 8] 【5x05】numpy.linspace()numpy.linspace() 方法用于创建一个一维数组,在指定的间隔内返回均匀间隔的数字并组成数组(Ndarray 对象),即该数组是一个等差数列构成的。linspace() 方法类似于 arange(),两者除了参数有差别以外,还有以下的区别: arange() 方法类似于内置函数 range(),通过指定开始值、终值和步长创建表示等差数列的一维数组,得到的结果数组不包含终值。 linspace() 通过指定开始值、终值和元素个数创建表示等差数列的一维数组,可以通过 endpoint 参数指定是否包含终值,默认值为True,即包含终值。 基本语法:numpy.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None, axis=0) 参数解释: 参数 描述 start 序列的起始值 stop 序列的终止值,如果 endpoint 为 True,则该值将包含于数列中 num 可选项,int 类型,要生成的等步长的样本数量,即元素个数,默认为 50 endpoint 可选项,bool 类型,该值为 True 时,数列中将包含 stop 值,反之则不包含,默认为 True retstep 可选项,bool 类型,该值为 True 时,生成的数组中会显示间距,反之则不显示,默认为 False dtype 可选项,Ndarray 的数据类型 axis 可选项,int 类型,结果中的轴用于存储样本。仅当 start 或 stop 类似于数组时才相关默认情况下为 0,采样将沿着在开始处插入的新轴进行,使用 -1 来获得轴的末端 应用举例: 不指定 num 值,将默认生成 50 个元素,数列中将包含 stop 值: 123456789101112>>> import numpy as np>>> a = np.linspace(1, 10)>>> print(a)[ 1. 1.18367347 1.36734694 1.55102041 1.73469388 1.91836735 2.10204082 2.28571429 2.46938776 2.65306122 2.83673469 3.02040816 3.20408163 3.3877551 3.57142857 3.75510204 3.93877551 4.12244898 4.30612245 4.48979592 4.67346939 4.85714286 5.04081633 5.2244898 5.40816327 5.59183673 5.7755102 5.95918367 6.14285714 6.32653061 6.51020408 6.69387755 6.87755102 7.06122449 7.24489796 7.42857143 7.6122449 7.79591837 7.97959184 8.16326531 8.34693878 8.53061224 8.71428571 8.89795918 9.08163265 9.26530612 9.44897959 9.63265306 9.81632653 10. ] 指定 num 值为 10,将生成 10 个元素,数列中将包含 stop 值: 1234>>> import numpy as np>>> a = np.linspace(1, 10, 10)>>> print(a)[ 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.] 指定 endpoint 值为 False,retstep 值为 True,数列中不包含 stop 值,生成的数组中会显示间距: 1234>>> import numpy as np>>> a = np.linspace(1, 10, 10, endpoint=False, retstep=True)>>> print(a)(array([1. , 1.9, 2.8, 3.7, 4.6, 5.5, 6.4, 7.3, 8.2, 9.1]), 0.9) 指定 dtype 类型为 int: 1234>>> import numpy as np>>> a = np.linspace(1, 10, 10, endpoint=False, retstep=True, dtype=int)>>> print(a)(array([1, 1, 2, 3, 4, 5, 6, 7, 8, 9]), 0.9) 【5x06】numpy.logspace()numpy.logspace() 方法用于创建一个一维数组,该数组由一个等比数列构成。 基本语法:numpy.logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None, axis=0) 参数解释: 参数 描述 start 序列的起始值 stop 序列的终止值,如果 endpoint 为 True,则该值将包含于数列中 num 可选项,int 类型,要生成的等步长的样本数量,即元素个数,默认为 50 endpoint 可选项,bool 类型,该值为 True 时,数列中将包含 stop 值,反之则不包含,默认为 True base 可选项,float 类型,对数 log 的底数,即取对数的时候 log 的下标 ,默认为 10.0 dtype 可选项,Ndarray 的数据类型 axis 可选项,int 类型,结果中的轴用于存储样本。仅当 start 或 stop 类似于数组时才相关默认情况下为 0,采样将沿着在开始处插入的新轴进行,使用 -1 来获得轴的末端 应用举例: 指定起始值为 0,,终止值为 9,base 默认值为 10,代表的是 10 的幂,即 0 代表 10 的 0 次方,9 代表 10 的 9 次方: 1234>>> import numpy as np>>> a = np.logspace(0, 9, num = 10)>>> print(a)[1.e+00 1.e+01 1.e+02 1.e+03 1.e+04 1.e+05 1.e+06 1.e+07 1.e+08 1.e+09] 指定起始值为 0,,终止值为 9,base 值为 2,代表的是 2 的幂,即 0 代表 2 的 0 次方,9 代表 2 的 9 次方: 1234>>> import numpy as np>>> a = np.logspace(0, 9, num = 10, base = 2)>>> print(a)[ 1. 2. 4. 8. 16. 32. 64. 128. 256. 512.] 起始值和终止值都可以为 float 类型: 12345>>> import numpy as np>>> a = np.logspace(1.0, 2.0, num = 10)>>> print(a)[ 10. 12.91549665 16.68100537 21.5443469 27.82559402 35.93813664 46.41588834 59.94842503 77.42636827 100. ] 定义 dtype 属性值为 int 类型: 1234>>> import numpy as np >>> a = np.logspace(0.0, 9.0, num = 10, base = 2, dtype = int)>>> print(a)[ 1 2 4 8 16 32 64 128 256 512] 【5x07】numpy.empty()numpy.empty() 方法可用来创建一个指定形状(shape)、数据类型(dtype)且未初始化的数组。 基本语法:numpy.empty(shape, dtype = float, order = 'C') 参数解释: 参数 描述 shape 数组形状 dtype 数据类型,可选 order 以行优先(C)或列优先(F)的顺序存储多维数据在内存中 创建一个一维空数组(传递一个参数即可,代表数组长度,数组元素为随机值,因为它们未初始化): 12345678910>>> import numpy as np>>> a = np.empty(3)>>> print(a)[3.538e-321 3.538e-321 0.000e+000]>>> print(type(a))<class 'numpy.ndarray'>>>>>>> a = np.empty(3, dtype = int) # 定义类型为整数>>> print(a)[716 0 716] 创建一个二维空数组(传递两个参数,分别代表行数和列数): 12345678>>> import numpy as np>>> a = np.empty([3, 2])>>> print(a)[[6.23042070e-307 3.56043053e-307] [1.37961641e-306 1.11258854e-306] [8.90100843e-307 1.11261027e-306]]>>> print(type(a))<class 'numpy.ndarray'> 创建一个三维空数组(传递三个参数,分别代表块数、每一块的行数和列数): 1234567891011>>> import numpy as np>>> a = np.empty([3, 2, 4])>>> print(a)[[[0. 0. 0. 0.] [0. 0. 0. 0.]] [[0. 0. 0. 0.] [0. 0. 0. 0.]] [[0. 0. 0. 0.] [0. 0. 0. 0.]]] 【5x08】numpy.zeros()numpy.zeros() 方法用于创建指定大小的数组,数组元素以 0 来填充。 基本语法:numpy.zeros(shape, dtype = float, order = 'C') 参数解释: 参数 描述 shape 数组形状 dtype 数据类型,可选 order 以行优先(C)或列优先(F)的顺序存储多维数据在内存中 创建一个一维数组(传递一个参数即可,代表数组长度,数组元素以 0 填充): 12345678910>>> import numpy as np>>> a = np.zeros(5)>>> print(a)[0. 0. 0. 0. 0.]>>> print(type(a))<class 'numpy.ndarray'>>>>>>> a = np.zeros(5, dtype = int) # 定义类型为整数>>> print(a)[0 0 0 0 0] 创建一个二维数组(传递两个参数,分别代表行数和列数): 1234567>>> import numpy as np>>> a = np.zeros([2, 3])>>> print(a)[[0. 0. 0.] [0. 0. 0.]]>>> print(type(a))<class 'numpy.ndarray'> 创建一个三维空数组(传递三个参数,分别代表块数、每一块的行数和列数): 12345678910111213141516>>> import numpy as np>>> a = np.zeros([4, 2, 3])>>> print(a)[[[0. 0. 0.] [0. 0. 0.]] [[0. 0. 0.] [0. 0. 0.]] [[0. 0. 0.] [0. 0. 0.]] [[0. 0. 0.] [0. 0. 0.]]]>>> print(type(a))<class 'numpy.ndarray'> 【5x09】numpy.ones()numpy.ones() 方法用于创建指定大小的数组,数组元素以 1 来填充。 基本语法:numpy.ones(shape, dtype = None, order = 'C') 参数解释: 参数 描述 shape 数组形状 dtype 数据类型,可选 order 以行优先(C)或列优先(F)的顺序存储多维数据在内存中 创建一个一维数组(传递一个参数即可,代表数组长度,数组元素以 0 填充): 123456789101112>>> import numpy as np>>> a = np.ones(5)>>> print(a)[1. 1. 1. 1. 1.]>>> print(type(a))<class 'numpy.ndarray'>>>> >>> a = np.ones(5, dtype = int) # 定义类型为整数>>> print(a)[1 1 1 1 1]>>> print(type(a))<class 'numpy.ndarray'> 创建一个二维数组(传递两个参数,分别代表行数和列数): 1234567>>> import numpy as np>>> a = np.ones([2, 3])>>> print(a)[[1. 1. 1.] [1. 1. 1.]]>>> print(type(a))<class 'numpy.ndarray'> 创建一个三维数组(传递三个参数,分别代表块数、每一块的行数和列数): 12345678910111213>>> import numpy as np>>> a = np.ones([3, 2 ,5])>>> print(a)[[[1. 1. 1. 1. 1.] [1. 1. 1. 1. 1.]] [[1. 1. 1. 1. 1.] [1. 1. 1. 1. 1.]] [[1. 1. 1. 1. 1.] [1. 1. 1. 1. 1.]]]>>> print(type(a))<class 'numpy.ndarray'> 【5x10】numpy.eye()numpy.eye() 方法用于创建对角矩阵数组,返回一个二维数组,对角线上值为 1,其余位置为 0。 基本语法:numpy.eye(N, M=None, k=0, dtype=<class 'float'>, order='C') 参数解释: 参数 描述 N int 类型,目标数组的行数 M int 类型,可选项,目标数组的列数,如果未指定,则默认与行数(N)相同 k int 类型,可选项,对角线索引,0(默认值)为主对角线,正值为上对角线,负值为下对角线简单来说可以理解成将值为 1 的对角线向左右平移 k 个单位,默认值 0 即对角线为 1,k 为正值右移,负值左移 dtype 可选项,返回数组的数据类型 order 可选项,以行优先(C)或列优先(F)的顺序存储多维数据在内存中 应用举例: 12345678910111213141516171819202122232425>>> import numpy as np>>> print(np.eye(5, 5)) # 创建一个对角矩阵[[1. 0. 0. 0. 0.] [0. 1. 0. 0. 0.] [0. 0. 1. 0. 0.] [0. 0. 0. 1. 0.] [0. 0. 0. 0. 1.]]>>> print(np.eye(5, 5, k=1)) # 将值为 1 的对角线向右移 1 个单位[[0. 1. 0. 0. 0.] [0. 0. 1. 0. 0.] [0. 0. 0. 1. 0.] [0. 0. 0. 0. 1.] [0. 0. 0. 0. 0.]]>>> print(np.eye(5, 5, k=-2)) # 将值为 1 的对角线向右左移 2 个单位[[0. 0. 0. 0. 0.] [0. 0. 0. 0. 0.] [1. 0. 0. 0. 0.] [0. 1. 0. 0. 0.] [0. 0. 1. 0. 0.]]>>> print(np.eye(5, dtype=int)) # 指定为 int 类型[[1 0 0 0 0] [0 1 0 0 0] [0 0 1 0 0] [0 0 0 1 0] [0 0 0 0 1]] 【5x11】numpy.frombuffer()numpy.frombuffer() 方法将缓冲区解释为一维数组,接受 buffer 输入参数,以流的形式读入转化成 ndarray 对象。当 buffer 是字符串时,Python3 默认 str 是 Unicode 类型,所以要转成 bytestring,即在原 str 前加上 b。 基本语法:numpy.frombuffer(buffer, dtype=float, count=-1, offset=0) 参数解释: 参数 描述 buffer 可以是任意对象,会以流的形式读入 dtype 可选项,返回数组的数据类型 count 可选项,读取的数据数量,默认为 -1,即读取缓冲区中所有数据 offset 可选项,读取的起始位置,以字节为单位,默认为 0 应用举例: 12345678910111213141516171819>>> import numpy as np>>> a = b'I love python!'>>> b = np.frombuffer(a, dtype='S1')>>> print(b)[b'I' b' ' b'l' b'o' b'v' b'e' b' ' b'p' b'y' b't' b'h' b'o' b'n' b'!']>>> >>> b = np.frombuffer(a, dtype='S1', count=5) # 指定要读取的数据量>>> print(b)[b'I' b' ' b'l' b'o' b'v']>>> >>> b = np.frombuffer(a, dtype='S1', count=5, offset=6) # 指定读取数据的起始位置>>> print(b)[b' ' b'p' b'y' b't' b'h']>>>>>> import numpy as np>>> a = b'\\x01\\x02'>>> b = np.frombuffer(a, dtype='uint8')>>> print(b)[1 2] 【5x12】numpy.fromiter()numpy.fromiter() 方法可以从可迭代对象中建立 Ndarray 对象,返回一个一维数组。 基本语法:numpy.fromiter(iterable, dtype, count=-1) 参数解释: 参数 描述 iterable 可迭代对象 dtype 返回数组的数据类型 count 读取的数据数量,默认为 -1,即读取所有数据 应用举例: 12345678910>>> import numpy as np>>> l = range(5)>>> i = iter(l) # iter() 方法用于生成迭代器>>> n = np.fromiter(i, dtype=float) # 从可迭代对象中建立 Ndarray 对象>>> print(l, type(l))range(0, 5) <class 'range'>>>> print(i, type(i))<range_iterator object at 0x00000163E75DCA70> <class 'range_iterator'>>>> print(n, type(n))[0. 1. 2. 3. 4.] <class 'numpy.ndarray'> 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/104870084未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃! 【6x00】改变数组的维度或者形状【6x01】numpy.reshape()numpy.reshape() 方法用于重新调整数组的维数(重塑)。 基本语法:numpy.reshape(a, newshape, order='C') 参数解释: a:要重塑的数组newshape:重塑后的形状,新形状应与原始形状兼容。如果是整数,则结果将是该长度的一维数组。一个形状维度可以是-1。在这种情况下,将根据数组的长度和剩余维度推断该值。举个例子,原数组 a 是一个 4 行 n 列的二维数组,现在要将其转换成只有 1 行的一维数组,由于不清楚原二维数组有多少列,也就不清楚一共有多少元素,所以可以使用 np.reshape(a, (1, -1)) 语句将其转化为一维数组,其中 -1 会让程序自动计算有多少列,此概念将在后面举例具体说明。order:可选值为 C、F、A,使用索引顺序读取 a 的元素,并按照索引顺序将元素放到变换后的的数组中,默认参数为 C。C 指的是用类 C 写的读/索引顺序的元素,最后一个维度变化最快,第一个维度变化最慢。横着读,横着写,优先读/写一行。F 是指用 FORTRAN 类索引顺序读/写元素,最后一个维度变化最慢,第一个维度变化最快。竖着读,竖着写,优先读/写一列。注意,C 和 F 选项不考虑底层数组的内存布局,只引用索引的顺序。A 选项所生成的数组的效果与原数组 a 的数据存储方式有关,如果数据是按照 FORTRAN 存储的话,它的生成效果与 F 相同,否则与 C 相同。应用举例:123456789101112131415>>> import numpy as np>>> a = np.array([1,2,3,4,5,6,7,8]) # 创建一个一维数组>>> print(a)[1 2 3 4 5 6 7 8]>>> b = np.reshape(a, (2,4)) # 重塑为一个二维数组>>> print(b)[[1 2 3 4] [5 6 7 8]]>>> c = np.reshape(a, (2,2,2)) # 重塑为一个三维数组>>> print(c)[[[1 2] [3 4]] [[5 6] [7 8]]]添加 order 参数举例:1234567891011>>> import numpy as np>>> a = np.array([[1,2,3], [4,5,6]]) # 创建一个二维数组>>> print(a)[[1 2 3] [4 5 6]]>>> b = np.reshape(a, 6, order='C') # 按照行优先>>> print(b)[1 2 3 4 5 6]>>> b = np.reshape(a, 6, order='F') # 按照列优先>>> print(b)[1 4 2 5 3 6]另外,reshape 方法新生成的数组和原数组共用一个内存,不管改变哪个都会互相影响:1234567891011121314>>> import numpy as np>>> a = np.array([1,2,3,4,5,6,7,8])>>> b = np.reshape(a, (2,4))>>> print(a)[1 2 3 4 5 6 7 8]>>> print(b)[[1 2 3 4] [5 6 7 8]]>>> a[0] = 666>>> print(a)[666 2 3 4 5 6 7 8]>>> print(b)[[666 2 3 4] [ 5 6 7 8]]newshape 重塑后的形状维度可以是 -1,简单举例:- reshape(1,-1):将原数组转化成一行 N 列- reshape(2,-1):将原数组转换成两行 N 列- reshape(-1,1):将原数组转换成一列 N 行- reshape(-1,2):将原数组转化成两列 N 行123456789101112131415161718192021222324>>> import numpy as np>>> a = np.arange(16) # 生成一个由 0-15 组成的一维数组>>> print(a)[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]>>> b = np.reshape(a, (2,8)) # 将一维数组 a 转换成一个 2 行 8 列的二维数组 b>>> print(b)[[ 0 1 2 3 4 5 6 7] [ 8 9 10 11 12 13 14 15]]>>> c = np.reshape(b, (8,-1)) # 将二维数组 b 转换成 8 行的格式,程序自动计算列数(列数:16/8=2)>>> print(c)[[ 0 1] [ 2 3] [ 4 5] [ 6 7] [ 8 9] [10 11] [12 13] [14 15]]>>> d = np.reshape(c, (-1,4)) # 将二维数组 c 转换成 4 列的格式,程序自动计算行数(行数:16/4=4)>>> print(d)[[ 0 1 2 3] [ 4 5 6 7] [ 8 9 10 11] [12 13 14 15]]### 【6x02】numpy.ravel() numpy.ravel() 方法用于完成展平的操作。 基本语法:numpy.ravel(a, order='C') 参数解释: 参数 描述 a 待转换的数组 order 值可以是 C F A K,含义与 reshape 方法中参数的一样,与 reshape 方法不同的是多了个值 KK 表示按顺序在内存中读取元素,但在跨距为负时会反转数据 应用举例: 123456789101112131415>>> import numpy as np>>> a = np.array([[[1,2,3,4], [5,6,7,8]], [[9,10,11,12], [13,14,15,16]], [[17,18,19,20], [21,22,23,24]]])>>> print(a)[[[ 1 2 3 4] [ 5 6 7 8]] [[ 9 10 11 12] [13 14 15 16]] [[17 18 19 20] [21 22 23 24]]]>>> print(a.ravel())[ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24]>>> print(np.ravel(a))[ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24] 【6x03】numpy.resize()numpy.resize() 方法会直接修改所操作的数组,返回具有指定形状的新数组,如果新数组大于原始数组,则新数组将填充 a 的重复副本。 基本语法:numpy.resize(a, new_shape) 参数解释: 参数 描述 a 待转换的数组 new_shape 新数组的大小形状 12345678910111213141516>>> import numpy as np>>> a = np.array([[1, 2, 3], [4, 5, 6]])>>> print(a)[[1 2 3] [4 5 6]]>>> print(np.resize(a, (3, 2)))[[1 2] [3 4] [5 6]]>>> print(np.resize(a, (3, 3)))[[1 2 3] [4 5 6] [1 2 3]]>>> print(np.resize(a, (2, 4)))[[1 2 3 4] [5 6 1 2]] 【6x04】numpy.ndarray.flatten()numpy.ndarray.flatten() 方法恰如其名,flatten 就是展平的意思,与 ravel 函数的功能相同,二者的不同之处在于:flatten 方法会请求分配新的内存来保存结果,而 ravel 方法只是返回数组的一个视图(view)。 基本语法:ndarray.flatten(order='C') 其 order 参数的值可以是 C F A K,含义与 reshape 和 ravel 方法中参数的一样. 应用举例: 12345678910111213>>> import numpy as np>>> a = np.array([[[1,2,3,4], [5,6,7,8]], [[9,10,11,12], [13,14,15,16]], [[17,18,19,20], [21,22,23,24]]])>>> print(a)[[[ 1 2 3 4] [ 5 6 7 8]] [[ 9 10 11 12] [13 14 15 16]] [[17 18 19 20] [21 22 23 24]]]>>> print(a.flatten())[ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24] 【6x05】numpy.ndarray.shapenumpy.ndarray.shape 本来是 Ndarray 对象的一个属性,但可以通过直接用一个正整数元组对其赋值来设置数组的维度: 12345678910111213141516>>> import numpy as np>>> a = np.array([[[1,2,3,4], [5,6,7,8]], [[9,10,11,12], [13,14,15,16]], [[17,18,19,20], [21,22,23,24]]])>>> print(a)[[[ 1 2 3 4] [ 5 6 7 8]] [[ 9 10 11 12] [13 14 15 16]] [[17 18 19 20] [21 22 23 24]]]>>> a.shape = (3, 8)>>> print(a)[[ 1 2 3 4 5 6 7 8] [ 9 10 11 12 13 14 15 16] [17 18 19 20 21 22 23 24]] 【6x06】numpy.ndarray.transpose() & numpy.ndarray.Tndarray.transpose() 和 ndarray.T 方法的作用是对数组进行转置,即原来的行变成列,原来的列变成行。 1234567891011121314151617181920>>> import numpy as np>>> a = np.array([[0, 1, 2, 3, 4, 5], [6, 7, 8, 9, 10, 11], [12, 13, 14, 15, 16, 17]])>>> print(a)[[ 0 1 2 3 4 5] [ 6 7 8 9 10 11] [12 13 14 15 16 17]]>>> print(a.transpose())[[ 0 6 12] [ 1 7 13] [ 2 8 14] [ 3 9 15] [ 4 10 16] [ 5 11 17]]>>> print(a.T)[[ 0 6 12] [ 1 7 13] [ 2 8 14] [ 3 9 15] [ 4 10 16] [ 5 11 17]] 【6x07】numpy.swapaxes()numpy.swapaxes() 方法用于对换数组的两个轴 基本语法:numpy.swapaxes(a, axis1, axis2) 参数解释:a 为原始数组,axis1、axis2 分别对应两个轴,类型为整数 12345678910111213>>> import numpy as np>>> a = np.array([[0, 1, 2, 3, 4, 5], [6, 7, 8, 9, 10, 11], [12, 13, 14, 15, 16, 17]])>>> print(a)[[ 0 1 2 3 4 5] [ 6 7 8 9 10 11] [12 13 14 15 16 17]]>>> print(np.swapaxes(a, 1, 0)) # 交换 1 轴和 0 轴,此处相当于数组的转置,与【6x06】效果相同[[ 0 6 12] [ 1 7 13] [ 2 8 14] [ 3 9 15] [ 4 10 16] [ 5 11 17]] 【7x00】NumPy 数据类型NumPy 数组包含同一类型的值,支持的数据类型比 Python 内置的类型更多,构建一个数组时,可以用一个字符串参数 dtype 来指定数据类型: 1np.zeros(10, dtype='int16') 1np.zeros(10, dtype=np.int16) 数据类型 描述 bool_               布尔值(True 或者 False),用一个字节存储 int_               默认的整型(类似于 C 语言中的 long,通常情况下是 int32 或 int64) intc               同 C 语言的 int 相同(通常是 int32 或 int64) intp               用作索引的整型(和 C 语言的 ssize_t 相同,通常情况下是 int32 或 int64) int8               字节(byte,范围从 –128 到 127),可用 i1 缩写代替 int16               整型(范围从 –32768 到 32767),可用 i2 缩写代替 int32               整型(范围从 –2147483648 到 2147483647),可用 i4 缩写代替 int64               整型(范围从 –9223372036854775808 到 9223372036854775807),可用 i8 缩写代替 uint8               无符号整型(范围从 0 到 255) uint16               无符号整型(范围从 0 到 65535) uint32               无符号整型(范围从 0 到 4294967295) uint64               无符号整型(范围从 0 到 18446744073709551615) float_               float64 的简化形式 float16               半精度浮点型,包括:1 比特位符号,5 比特位指数,10 比特位尾数 float32               单精度浮点型,包括:1 比特位符号,8 比特位指数,23 比特位尾数 float64               双精度浮点型,包括:1 比特位符号,11 比特位指数,52 比特位尾数 complex_               complex128 的简化形式 complex64               复数,表示双 32 位浮点数(实数部分和虚数部分) complex128               复数,表示双 64 位浮点数(实数部分和虚数部分) 【8x00】NumPy 数组属性 属性 描述 ndarray.ndim 秩,即轴的数量或维度的数量,一维数组的秩为 1,二维数组的秩为 2,以此类推 ndarray.shape 数组的维度,对于矩阵,n 行 m 列 ndarray.size 数组元素的总个数,相当于 .shape 中 n*m 的值 ndarray.dtype ndarray 对象的元素类型 ndarray.itemsize ndarray 对象中每个元素的大小,以字节为单位 ndarray.flags ndarray 对象的内存信息 ndarray.real ndarray元素的实部 ndarray.imag ndarray 元素的虚部 ndarray.data 包含实际数组元素的缓冲区,由于一般通过数组的索引获取元素,所以通常不需要使用这个属性 其中 ndarray.flags 包含以下属性: 属性 描述 C_CONTIGUOUS (C) 数据是在一个单一的 C 风格的连续段中 F_CONTIGUOUS (F) 数据是在一个单一的 Fortran 风格的连续段中 OWNDATA (O) 数组拥有它所使用的内存或从另一个对象中借用它 WRITEABLE (W) 数据区域可以被写入,将该值设置为 False,则数据为只读 ALIGNED (A) 数据和所有元素都适当地对齐到硬件上 UPDATEIFCOPY (U) 这个数组是其它数组的一个副本,当这个数组被释放时,原数组的内容将被更新 应用举例: 12345678910>>> import numpy as np>>> a = np.array([1,2,3,4,5])>>> print(a.flags) C_CONTIGUOUS : True F_CONTIGUOUS : True OWNDATA : True WRITEABLE : True ALIGNED : True WRITEBACKIFCOPY : False UPDATEIFCOPY : False ndarray.shape 查看数组维度以及更改数组形状: 1234567891011121314>>> import numpy as np>>> a = np.array([[1,2,3],[4,5,6]])>>> print(a)[[1 2 3] [4 5 6]]>>> print(a.shape)(2, 3)>>> a.shape = (3, 2)>>> print(a)[[1 2] [3 4] [5 6]]>>> print(a.shape)(3, 2) 12345这里是一段防爬虫文本,请读者忽略。本文原创首发于 CSDN,作者 TRHX。博客首页:https://itrhx.blog.csdn.net/本文链接:https://itrhx.blog.csdn.net/article/details/104870084未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!","categories":[{"name":"Python 数据分析","slug":"Python-数据分析","permalink":"https://www.itrhx.com/categories/Python-数据分析/"},{"name":"NumPy","slug":"Python-数据分析/NumPy","permalink":"https://www.itrhx.com/categories/Python-数据分析/NumPy/"}],"tags":[{"name":"NumPy","slug":"NumPy","permalink":"https://www.itrhx.com/tags/NumPy/"},{"name":"数组","slug":"数组","permalink":"https://www.itrhx.com/tags/数组/"}]},{"title":"用 VPS 搭建一个自己的 SSR 服务器","slug":"A61-build-a-SSR-server-with-VPS","date":"2020-01-10T13:38:13.786Z","updated":"2020-03-31T09:27:43.584Z","comments":true,"path":"2020/01/10/A61-build-a-SSR-server-with-VPS/","link":"","permalink":"https://www.itrhx.com/2020/01/10/A61-build-a-SSR-server-with-VPS/","excerpt":"俗话说得好:预先善其事,必先利其器,作为一个程序员,经常会用到 GitHub、Google、Stack Overflow 啥的,由于国内政策原因,想要访问国外网站就得科学上网,最常见的工具就是 ShadowsocksR,又被称为酸酸乳、SSR、小飞机,目前市面上有很多很多的机场,价格也不是很高,完全可以订阅别人的,但是订阅别人的,数据安全没有保障,有可能你的浏览历史啥的别人都能掌握,别人也有随时跑路的可能,总之,只有完全属于自己的东西才是最香的!","text":"俗话说得好:预先善其事,必先利其器,作为一个程序员,经常会用到 GitHub、Google、Stack Overflow 啥的,由于国内政策原因,想要访问国外网站就得科学上网,最常见的工具就是 ShadowsocksR,又被称为酸酸乳、SSR、小飞机,目前市面上有很多很多的机场,价格也不是很高,完全可以订阅别人的,但是订阅别人的,数据安全没有保障,有可能你的浏览历史啥的别人都能掌握,别人也有随时跑路的可能,总之,只有完全属于自己的东西才是最香的! 购买 VPSVPS(Virtual Private Server)即虚拟专用服务器技术,在购买 VPS 服务器的时候要选择国外的,推荐 Vultr,国际知名,性价比比较高,最低有$2.5/月、$3.5/月的,个人用的话应该足够了。 点击链接注册 Vultr 账号:https://www.vultr.com/?ref=8367048,目前新注册用户充值10刀可以赠送50刀,注册完毕之后来到充值页面,最低充值10刀,可以选择支付宝或者微信支付。 充值完毕之后,点击左侧 Products,选择服务器,一共有16个地区的,选择不同地区的服务器,最后的网速也有差别,那如何选择一个速度最优的呢?很简单,你可以一次性选择多个服务器,都部署上去,搭建完毕之后,测试其速度,选择最快的,最后再把其他的都删了,可能你会想,部署多个,那费用岂不是很贵,这里注意,虽然写的是多少钱一个月,而实际上它是按照小时计费的,从你部署之后开始计费,$5/月 ≈ $0.00694/小时,你部署完毕再删掉,这段时间的费用很低,可以忽略不计,一般来说,日本和新加坡的比较快一点,也有人说日本和新加坡服务器的端口封得比较多,容易搭建失败,具体可以自己测试一下,还有就是,只有部分地区的服务器有$2.5/月、$3.5/月的套餐,其中$2.5/月的只支持 IPv6,可以根据自己情况选择,最后操作系统建议选择 CentOS 7 x64 的,不然有可能搭建失败,后面还有个 Enable IPv6 的选项,对 IPv6 有需求的话可以勾上,其他选项就可以不用管了。 部署成功后,点 Server Details 可以看到服务器的详细信息,其中有 IP、用户名、密码等信息,后面搭建 SSR 的时候会用到,此时你可以 ping 一下你的服务器 IP,如果 ping 不通的话,可以删掉再重新开一个服务器。 搭建 SSR我们购买的是虚拟的服务器,因此需要工具远程连接到 VPS,如果是 Mac/Linux 系统,可以直接在终端用 SSH 连接 VPS: 1ssh root@你VPS的IP -p 22 (22是你VPS的SSH端口) 如果是 Windows 系统,可以用第三方工具连接到 VPS,如:Xshell、Putty 等,可以百度下载,以下以 Xshell 为例: 点击文件,新建会话,名称可以随便填,协议为 SSH,主机为你服务器的 IP 地址,点击确定,左侧双击这个会话开始连接,最开始会出现一个 SSH安全警告,点击接受并保存即可,然后会让你输入服务器的用户名和密码,直接在 Vultr 那边复制过来即可,最后看到 [root@vultr ~]# 字样表示连接成功。 连接成功后执行以下命令开始安装 SSR: 1wget --no-check-certificate https://freed.ga/github/shadowsocksR.sh; bash shadowsocksR.sh 如果提示 wget :command not found,可先执行 yum -y install wget,再执行上述命令即可。 执行完毕后会让你设置 SSR 连接密码和端口,然后按任意键开始搭建。 搭建成功后会显示你服务器 IP,端口,连接密码,协议等信息,这些信息要记住,后面使用 ShadowsocksR 的时候要用到。 安装锐速由于我们购买的服务器位于国外,如果遇到上网高峰期,速度就会变慢,而锐速就是一款专业的连接加速器,可以充分利用服务器带宽,提升带宽吞吐量,其他还有类似的程序如 Google BBR 等,可以自行比较其加速效果,以下以操作系统为 CentOS 6&7 锐速的安装为例。 如果你服务器操作系统选择的是 CentOS 6 x64,则直接执行以下命令,一直回车即可: 1wget --no-check-certificate -O appex.sh https://raw.githubusercontent.com/hombo125/doubi/master/appex.sh && bash appex.sh install '2.6.32-642.el6.x86_64' 如果你服务器操作系统选择的是 CentOS 7 x64,则需要先执行以下命令更换内核: 1wget --no-check-certificate -O rskernel.sh https://raw.githubusercontent.com/hombo125/doubi/master/rskernel.sh && bash rskernel.sh 如下图所示表示内核更换完毕,此时已经断开与服务器的连接,我们需要重新连接到服务器,再执行后面的操作: 重新连接到服务器后,再执行以下命令: 1yum install net-tools -y && wget --no-check-certificate -O appex.sh https://raw.githubusercontent.com/0oVicero0/serverSpeeder_Install/master/appex.sh && bash appex.sh install 然后一直回车即可,系统会自动安装锐速。 出现以下信息表示安装成功: 使用 SSR常见的工具有 ShadowsocksR、SSTap(原本是个游戏加速器,现在已经停止维护,但 GitHub 上仍然可以找到)等。 Shadowsocks 官网:https://shadowsocks.org/ShadowsocksR 下载地址:https://github.com/Anankke/SSRR-WindowsSSTap GitHub 地址:https://github.com/FQrabbit/SSTap-Rule 不管什么工具,用法都是一样的,添加一个新的代理服务器,服务器 IP、端口、密码、加密方式等等这些信息保持一致就行了。然后就可以愉快地科学上网了! 多端口配置经过以上步骤我们就可以科学上网了,但是目前为止只有一个端口,只能一个人用,那么如何实现多个端口多人使用呢?事实上端口、密码等信息是储存在一个叫做 shadowsocks.json 文件里的,如果要添加端口或者更改密码,只需要修改此文件即可。 连接到自己的 VPS,输入以下命令,使用 vim 编辑文件:vi /etc/shadowsocks.json 原文件内容大概如下: 1234567891011121314151617181920{ \"server\": \"0.0.0.0\", \"server_port\": 8686, \"server_ipv6\": \"::\", \"local_address\": \"127.0.0.1\", \"local_port\": 1081, \"password\":\"SSR12345\", \"timeout\": 120, \"udp_timeout\": 60, \"method\": \"aes-256-cfb\", \"protocol\": \"auth_sha1_v4_compatible\", \"protocol_param\": \"\", \"obfs\": \"http_simple_compatible\", \"obfs_param\": \"\", \"dns_ipv6\": false, \"connect_verbose_info\": 1, \"redirect\": \"\", \"fast_open\": false, \"workers\": 1} 增加端口,我们将其修改为如下内容: 1234567891011121314151617181920212223242526{ \"server\": \"0.0.0.0\", \"server_ipv6\": \"::\", \"local_address\": \"127.0.0.1\", \"local_port\": 1081, \"port_password\": { \"8686\":\"SSR1\", \"8687\":\"SSR2\", \"8688\":\"SSR3\", \"8689\":\"SSR4\", \"8690\":\"SSR5\" }, \"timeout\": 120, \"udp_timeout\": 60, \"method\": \"aes-256-cfb\", \"protocol\": \"auth_sha1_v4_compatible\", \"protocol_param\": \"\", \"obfs\": \"http_simple_compatible\", \"obfs_param\": \"\", \"dns_ipv6\": false, \"connect_verbose_info\": 1, \"redirect\": \"\", \"fast_open\": false, \"workers\": 1} 也就是删除原来的 server_port 和 password 这两项,然后增加 port_password 这一项,前面是端口号,后面是密码,注意不要把格式改错了!!!修改完毕并保存!!! 接下来配置一下防火墙,同样的,输入以下命令,用 vim 编辑文件:vi /etc/firewalld/zones/public.xml 初始的防火墙只开放了最初配置 SSR 默认的那个端口,现在需要我们手动加上那几个新加的端口,注意:一个端口需要复制两行,一行是 tcp,一行是 udp。 原文件内容大概如下: 12345678<?xml version=\"1.0\" encoding=\"utf-8\"?><zone> <short>Public</short> <service name=\"dhcpv6-client\"/> <service name=\"ssh\"/> <port protocol=\"tcp\" port=\"8686\"/> <port protocol=\"udp\" port=\"8686\"/></zone> 修改后的内容如下: 12345678910111213141516<?xml version=\"1.0\" encoding=\"utf-8\"?><zone> <short>Public</short> <service name=\"dhcpv6-client\"/> <service name=\"ssh\"/> <port protocol=\"tcp\" port=\"8686\"/> <port protocol=\"udp\" port=\"8686\"/> <port protocol=\"tcp\" port=\"8687\"/> <port protocol=\"udp\" port=\"8687\"/> <port protocol=\"tcp\" port=\"8688\"/> <port protocol=\"udp\" port=\"8688\"/> <port protocol=\"tcp\" port=\"8689\"/> <port protocol=\"udp\" port=\"8689\"/> <port protocol=\"tcp\" port=\"8690\"/> <port protocol=\"udp\" port=\"8690\"/></zone> 修改完毕并保存,最后重启一下 shadowsocks,然后重新载入防火墙即可,两条命令如下: 1/etc/init.d/shadowsocks restart 1firewall-cmd --reload 完成之后,我们新加的这几个端口就可以使用了 另外还可以将配置转换成我们常见的链接形式,如:ss://xxxxx 或 ssr://xxxxx,其实这种链接就是把 IP,端口,密码等信息按照一定的格式拼接起来,然后经过 Base64 编码后实现的,有兴趣或者有需求的可以自行百度。 扩展命令SSR 常用命令:启动:/etc/init.d/shadowsocks start停止:/etc/init.d/shadowsocks stop重启:/etc/init.d/shadowsocks restart状态:/etc/init.d/shadowsocks status卸载:./shadowsocks-all.sh uninstall更改配置参数:vim /etc/shadowsocks-r/config.json 我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=mpa02hyz07v5","categories":[{"name":"VPS","slug":"VPS","permalink":"https://www.itrhx.com/categories/VPS/"}],"tags":[{"name":"VPS","slug":"VPS","permalink":"https://www.itrhx.com/tags/VPS/"},{"name":"SSR","slug":"SSR","permalink":"https://www.itrhx.com/tags/SSR/"}]},{"title":"2019年总结【跨越今天,更不平凡】","slug":"A60-2019-summary","date":"2019-12-31T15:14:59.983Z","updated":"2020-08-06T02:59:52.727Z","comments":true,"path":"2019/12/31/A60-2019-summary/","link":"","permalink":"https://www.itrhx.com/2019/12/31/A60-2019-summary/","excerpt":"","text":"还记得小时候写作文,畅想2020会怎样怎样,光阴似箭,2020真的来了,度过了艰难的考试周,抽了个晚上,回想了一下,决定写一写总结吧,似乎以前都没写过呢,那干脆连带2017、2018也写写吧,重点写一写2019的,以后争取每年都做一下总结。 【2017】2017年高三,上半年就不用说了,所有高三考生都一个样吧,下半年考进了武汉的某二本院校,软件工程专业,现在回想起来,当时把时间浪费得太多了,最开始加了一个部门,后来退了(事实上啥也学不到,浪费时间 ),然后除了完成学校的课程以外,其他啥也没搞,剩下的时间基本上全拿来骑车了,从高一开始就热爱单车运动,刚上大学肯定得放飞自我了,没课的时候就天天和学长到处跑,都快把武汉跑了个遍了,当时还定了个计划,大学四年骑车去一次西藏或者青海湖,其他的什么都没想,也没有对以后具体干哪方面做过规划,这一年收获最多的应该就是路上的风景了。 【2018】2018上半年,大一下学期,学习方面就过了个英语四级,然后依旧热衷于我的单车,暑假的时候疯狂了一把,7天干了700多公里,从学校骑回家了,那个时候正是热的时候,白天基本上在三十度,从武汉往西边走,后面全是爬山,上山爬不动,下山刹不住,路上也遇到了不少牛逼人物,有徒步西藏的,有环游中国的,直播平台有好几十万粉丝的……遇到的人都很善良,很硬汉,这次经历从某种程度上来说也是一次成长吧,一次很有意义的骑行。 下半年,也就是大二开始,才慢慢开始重视专业知识的学习,大二上学期搭建了个人博客,开始尝试写博客,其实就是把博客当做笔记吧,记性不好,学了的东西容易忘记,忘记了可以经常翻自己博客再复习复习,自己踩过的坑也记录记录,后来没想到有些文章访问量还挺高的,在博客搭建方面也帮到了一些网友,最重要的是结识了不少博友,有各行各业的大佬,下半年也定了方向,开始专注Python的学习,从此开始慢慢熬夜,也渐渐地不怎么出去骑车了。 【2019】2019 总的来说,还比较满意吧,主要是感觉过得很充实,大三基本上每天一整天都是上机课,没有太多时间搞自己的,自己倾向于Python、网络爬虫、数据分析方面,然而这些课程学校都没有,每天晚上以及周六周日都是自己在学,找了不少视频在看,有时候感觉自己还是差点火候,感觉一个简单的东西人家看一遍就会,但是我要看好几遍,不管怎样,我还是相信勤能补拙的。 【学习方面】 [√] 通过软考中级软件设计师 [√] 成为入党积极分子 [√] 学校大课基金结题 英语六级未通过 国家专利未通过 【看完或者大部分看完的书籍】 [√] 《软件设计师考试》 [√] 《Python 编程从入门到实践》 [√] 《Python 编程从零基础到项目实战》 [√] 《Python3 网络爬虫开发实战》 [√] 《Python 网络爬虫从入门到实践》 [√] 《精通 Python 爬虫框架 Scrapy》 [√] 《Python 程序员面试宝典(算法+数据结构)》 [√] 《Selenium 自动化测试 — 基于 Python 语言》 [√] 《重构,改善既有代码的设计》 【生活方面】暑假受家族前辈的邀请,为整个姓氏家族编写族谱,感觉这是今年收获最大的一件事情吧,当时背着电脑跟着前辈下乡,挨家挨户统计资料,纯手工录入电脑(感觉那是我活了二十年打字打得最多的一个月,祖宗十八代都搞清楚了),最后排版打印成书,一个月下来感受到了信息化时代和传统文化的碰撞,见了很多古书,古迹,当然还领略到了古繁体字的魅力,前辈一路上给我讲述了很多书本上学不到的东西,一段很有意义的体验,感触颇深。 个人爱好上面,今年就基本上没有骑车了,没有经常骑车,开学骑了两次就跟不上别人了,后面就洗干净用布遮起来放在寝室了,按照目前情况来看,多半是要“退役”了,不知道何时才会又一次踩上脚踏,不过偶尔还是在抖音上刷刷关注的单车大佬,看看别人的视频,看到友链小伙伴 Shan San 在今年总结也写了他一年没有跳舞了,抛弃了曾经热爱的 Breaking,真的是深有感触啊。 有个遗憾就是大一的愿望实现不了了,恐怕大学四年也不会去西藏或者青海湖了,此处放一个到目前为止的骑行数据,以此纪念一下我的单车生涯吧。 【技术交流&实践】自从搭建了博客之后,认识了不少大佬,经常会去大佬博客逛逛,涨涨知识 截止目前,个人博客 PV:4万+,UV:1万+,知乎:400+赞同,CSDN:43万+访问量,400+赞同 此外今年第一次为开源做了一点儿微不足道的贡献,为 Hexo 博客主题 Material X 添加了文章字数统计和阅读时长的功能,提交了人生当中第一个 PR。第一次嘛,还是值得纪念一下的。 我 GitHub 上虽然有一些小绿点,但是很大一部分都是推送的博客相关的东西,剩下的有几个仓库也就是 Python 相关的了,一些实战的代码放在了上面,很多时候是拿 GitHub 围观一些牛逼代码或者资源,还需要努力学习啊! 实战方面,爬虫自己也爬了很多网站,遇到一些反爬网站还不能解决,也刷了一些 Checkio 上面的题,做了题,和其他大佬相比才会发现自己的代码水平有多低,最直接的感受就是我用了很多行代码,而大神一行代码就解决了,只能说自己的水平还有很大的增进空间,新的一年继续努力吧! 【2020】1024 + 996 = 2020,2020注定是不平凡的一年,定下目标,努力实现,只谈技术,莫问前程! 【计划目标】 4月蓝桥杯拿奖 5月通过软考高级信息系统项目管理师 6月通过英语六级 坚持记笔记、写博客 学习 JavaScript 逆向 研究网站常用反爬策略,掌握反反爬虫技术 掌握两到三个主流爬虫框架 加深 Python 算法和数据结构的学习 学习 Python 数据可视化和数据分析 做一个 Python 相关的优秀开源项目(爬虫类最好) 向优秀爬虫工程师方向迈进 参加 PyCon China 2020 【计划要看的书籍】 《JavaScript 从入门到精通》 《Python3 反爬虫原理与绕过实战》 《Python 数据可视化编程实战》 《Python 数据可视化之 matplotlib 实践》 《Python 数据可视化之 matplotlib 精进》 《基于 Python的大数据分析基础及实战》 123>>> pip uninstall 2019>>> pip install 2020>>> print('Live a good life, write some good code !!!')","categories":[{"name":"BLOG","slug":"BLOG","permalink":"https://www.itrhx.com/categories/BLOG/"}],"tags":[{"name":"年终总结","slug":"年终总结","permalink":"https://www.itrhx.com/tags/年终总结/"}]},{"title":"Python3 爬虫实战 — 瓜子全国二手车","slug":"A59-pyspider-guazi","date":"2019-11-14T16:10:55.649Z","updated":"2019-12-29T07:14:02.938Z","comments":true,"path":"2019/11/15/A59-pyspider-guazi/","link":"","permalink":"https://www.itrhx.com/2019/11/15/A59-pyspider-guazi/","excerpt":"爬取时间:2019-11-14爬取难度:★★☆☆☆☆请求链接:https://www.guazi.com/www/buy/爬取目标:爬取瓜子全国二手车信息,包括价格、上牌时间、表显里程等;保存车辆图片涉及知识:请求库 requests、解析库 lxml、Xpath 语法、数据库 MongoDB 的操作完整代码:https://github.com/TRHX/Python3-Spider-Practice/tree/master/guazi其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278","text":"爬取时间:2019-11-14爬取难度:★★☆☆☆☆请求链接:https://www.guazi.com/www/buy/爬取目标:爬取瓜子全国二手车信息,包括价格、上牌时间、表显里程等;保存车辆图片涉及知识:请求库 requests、解析库 lxml、Xpath 语法、数据库 MongoDB 的操作完整代码:https://github.com/TRHX/Python3-Spider-Practice/tree/master/guazi其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278 【1x00】提取所有二手车详情页URL分析页面,按照习惯,最开始在 headers 里面只加入 User-Agent 字段,向主页发送请求,然而返回的东西并不是主页真正的源码,因此我们加入 Cookie,再次发起请求,即可得到真实数据。 获取 Cookie:打开浏览器访问网站,打开开发工具,切换到 Network 选项卡,筛选 Doc 文件,在 Request Headers 里可以看到 Cookie 值。 注意在爬取瓜子二手车的时候,User-Agent 与 Cookie 要对应一致,也就是直接复制 Request Headers 里的 User-Agent 和 Cookie,不要自己定义一个 User-Agent,不然有可能获取不到信息! 分析页面,请求地址为:https://www.guazi.com/www/buy/ 第一页:https://www.guazi.com/www/buy/ 第二页:https://www.guazi.com/www/buy/o2c-1/ 第三页:https://www.guazi.com/www/buy/o3c-1/ 一共有50页数据,利用 for 循环,每次改变 URL 中 o2c-1 参数里面的数字即可实现所有页面的爬取,由于我们是想爬取每台二手车详情页的数据,所以定义一个 parse_index() 函数,提取每一页的所有详情页的 URL,保存在列表 url_list 中 1234567891011121314151617181920# 必须要有 Cookie 和 User-Agent,且两者必须对应(用浏览器访问网站后控制台里面复制)headers = { 'Cookie': 'uuid=06ce7520-ebd1-45bc-f41f-a95f2c9b2283; ganji_uuid=7044571161649671972745; lg=1; clueSourceCode=%2A%2300; user_city_id=-1; sessionid=fefbd4f8-0a06-4e8a-dc49-8856e1a02a07; Hm_lvt_936a6d5df3f3d309bda39e92da3dd52f=1573469368,1573541270,1573541964,1573715863; close_finance_popup=2019-11-14; cainfo=%7B%22ca_a%22%3A%22-%22%2C%22ca_b%22%3A%22-%22%2C%22ca_s%22%3A%22seo_baidu%22%2C%22ca_n%22%3A%22default%22%2C%22ca_medium%22%3A%22-%22%2C%22ca_term%22%3A%22-%22%2C%22ca_content%22%3A%22-%22%2C%22ca_campaign%22%3A%22-%22%2C%22ca_kw%22%3A%22-%22%2C%22ca_i%22%3A%22-%22%2C%22scode%22%3A%22-%22%2C%22keyword%22%3A%22-%22%2C%22ca_keywordid%22%3A%22-%22%2C%22display_finance_flag%22%3A%22-%22%2C%22platform%22%3A%221%22%2C%22version%22%3A1%2C%22client_ab%22%3A%22-%22%2C%22guid%22%3A%2206ce7520-ebd1-45bc-f41f-a95f2c9b2283%22%2C%22ca_city%22%3A%22wh%22%2C%22sessionid%22%3A%22fefbd4f8-0a06-4e8a-dc49-8856e1a02a07%22%7D; _gl_tracker=%7B%22ca_source%22%3A%22-%22%2C%22ca_name%22%3A%22-%22%2C%22ca_kw%22%3A%22-%22%2C%22ca_id%22%3A%22-%22%2C%22ca_s%22%3A%22self%22%2C%22ca_n%22%3A%22-%22%2C%22ca_i%22%3A%22-%22%2C%22sid%22%3A56473912809%7D; cityDomain=www; preTime=%7B%22last%22%3A1573720945%2C%22this%22%3A1573469364%2C%22pre%22%3A1573469364%7D; Hm_lpvt_936a6d5df3f3d309bda39e92da3dd52f=1573720946; rfnl=https://www.guazi.com/www/chevrolet/i2c-1r18/; antipas=675i0t513a7447M2L9y418Qq869', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36'}# 获取所有二手车详情页URLdef parse_index(): response = requests.get(url=url, headers=headers) tree = etree.HTML(response.text) url_list = tree.xpath('//li/a[@class=\"car-a\"]/@href') # print(len(url_list)) return url_listif __name__ == '__main__': for i in range(1, 51): url = 'https://www.guazi.com/www/buy/o%sc-1/' % i detail_urls = parse_index() 【2x00】获取二手车详细信息并保存图片前面的第一步我们已经获取到了二手车详情页的 URL,现在定义一个 parse_detail() 函数,向其中循环传入每一条 URL,利用 Xpath 语法匹配每一条信息,所有信息包含:标题、二手车价格、新车指导价、车主、上牌时间、表显里程、上牌地、排放标准、变速箱、排量、过户次数、看车地点、年检到期、交强险、商业险到期。 其中有部分信息可能包含空格,可以用 strip() 方法将其去掉。 需要注意的是,上牌地对应的是一个 class="three" 的 li 标签,有些二手车没有上牌地信息,匹配的结果将是空,在数据储存时就有可能出现数组越界的错误信息,所以这里可以加一个判断,如果没有上牌地信息,可以将其赋值为:未知。 保存车辆图片时,为了节省时间和空间,避免频繁爬取被封,所以只保存第一张图片,同样利用 Xpath 匹配到第一张图片的地址,以标题为图片的名称,定义储存路径后,以二进制形式保存图片。 最后整个函数返回的是一个列表 data,这个列表包含每辆二手车的所有信息 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113# 获取二手车详细信息def parse_detail(content): detail_response = requests.get(url=content, headers=headers) tree = etree.HTML(detail_response.text) # 标题 title = tree.xpath('//h2[@class=\"titlebox\"]/text()') # 移除字符串头尾空格 title = [t.strip() for t in title] # 匹配到两个元素,只取其中一个为标题 title = title[:1] # print(title) # 价格 price_old = tree.xpath('//span[@class=\"pricestype\"]/text()') # 移除字符串头尾空格 price_old = [p.strip() for p in price_old] # 加入单位 price_old = [''.join(price_old + ['万'])] # print(price_old) # 新车指导价 price_new = tree.xpath('//span[@class=\"newcarprice\"]/text()') # 移除字符串头尾空格 price_new = [p.strip() for p in price_new] # 对字符串进行切片,只取数字多少万 price_new = ['¥' + price_new[0].split('价')[1]] # print(price_new) # 车主 owner = tree.xpath('//dl/dt/span/text()') owner = [owner[0].replace('车主:', '')] # print(owner) # 上牌时间 spsj = tree.xpath('//li[@class=\"one\"]/div/text()') # print(spsj) # 表显里程 bxlc = tree.xpath('//li[@class=\"two\"]/div/text()') # print(bxlc) # 上牌地 spd = tree.xpath('//li[@class=\"three\"]/div/text()') # 某些二手车没有上牌地,没有的将其赋值为:未知 if len(spd) == 0: spd = ['未知'] # print(spd) # 排放标准 pfbz = tree.xpath('//li[@class=\"four\"]/div/text()') pfbz = pfbz[:1] # print(pfbz) # 变速箱 bsx = tree.xpath('//li[@class=\"five\"]/div/text()') # print(bsx) # 排量 pl = tree.xpath('//li[@class=\"six\"]/div/text()') # print(pl) # 过户次数 ghcs = tree.xpath('//li[@class=\"seven\"]/div/text()') ghcs = [g.strip() for g in ghcs] ghcs = ghcs[:1] # print(ghcs) # 看车地点 kcdd = tree.xpath('//li[@class=\"eight\"]/div/text()') # print(kcdd) # 年检到期 njdq = tree.xpath('//li[@class=\"nine\"]/div/text()') # print(njdq) # 交强险 jqx = tree.xpath('//li[@class=\"ten\"]/div/text()') # print(jqx) # 商业险到期 syxdq = tree.xpath('//li[@class=\"last\"]/div/text()') syxdq = [s.strip() for s in syxdq] syxdq = syxdq[:1] # print(syxdq) # 保存车辆图片 # 获取图片地址 pic_url = tree.xpath('//li[@class=\"js-bigpic\"]/img/@data-src')[0] pic_response = requests.get(pic_url) # 定义图片名称以及保存的文件夹 pic_name = title[0] + '.jpg' dir_name = 'guazi_pic' # 如果没有该文件夹则创建该文件夹 if not os.path.exists(dir_name): os.mkdir(dir_name) # 定义储存路径 pic_path = dir_name + '/' + pic_name with open(pic_path, \"wb\")as f: f.write(pic_response.content) # 将每辆二手车的所有信息合并为一个列表 data = title + price_old + price_new + owner + spsj + bxlc + spd + pfbz + bsx + pl + ghcs + kcdd + njdq + jqx + syxdq return dataif __name__ == '__main__': for i in range(1, 51): url = 'https://www.guazi.com/www/buy/o%sc-1/' % i detail_urls = parse_index() for detail_url in detail_urls: car_url = 'https://www.guazi.com' + detail_url car_data = parse_detail(car_url) 【3x00】将数据储存到 MongoDB定义数据储存函数 save_data() 使用 MongoClient() 方法,向其传入地址参数 host 和 端口参数 port,指定数据库为 guazi,集合为 esc 传入第二步 parse_detail() 函数返回的二手车信息的列表,依次读取其中的元素,每一个元素对应相应的信息名称 最后调用 insert_one() 方法,每次插入一辆二手车的数据 12345678910111213141516171819202122232425262728293031323334353637# 将数据储存到 MongoDBdef save_data(data): client = pymongo.MongoClient(host='localhost', port=27017) db = client.guazi collection = db.esc esc = { '标题': data[0], '二手车价格': data[1], '新车指导价': data[2], '车主': data[3], '上牌时间': data[4], '表显里程': data[5], '上牌地': data[6], '排放标准': data[7], '变速箱': data[8], '排量': data[9], '过户次数': data[10], '看车地点': data[11], '年检到期': data[12], '交强险': data[13], '商业险到期': data[14] } collection.insert_one(esc)if __name__ == '__main__': for i in range(1, 51): url = 'https://www.guazi.com/www/buy/o%sc-1/' % i detail_urls = parse_index() for detail_url in detail_urls: car_url = 'https://www.guazi.com' + detail_url car_data = parse_detail(car_url) save_data(car_data) # 在3-10秒之间随机暂停 time.sleep(random.randint(3, 10)) time.sleep(random.randint(5, 60)) print('所有数据爬取完毕!') 【4x00】完整代码123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182# =============================================# --*-- coding: utf-8 --*--# @Time : 2019-11-14# @Author : TRHX# @Blog : www.itrhx.com# @CSDN : https://blog.csdn.net/qq_36759224# @FileName: guazi.py# @Software: PyCharm# =============================================from lxml import etreeimport requestsimport pymongoimport timeimport randomimport os# 必须要有 Cookie 和 User-Agent,且两者必须对应(用浏览器访问网站后控制台里面复制)headers = { 'Cookie': 'uuid=06ce7520-ebd1-45bc-f41f-a95f2c9b2283; ganji_uuid=7044571161649671972745; lg=1; clueSourceCode=%2A%2300; user_city_id=-1; sessionid=fefbd4f8-0a06-4e8a-dc49-8856e1a02a07; Hm_lvt_936a6d5df3f3d309bda39e92da3dd52f=1573469368,1573541270,1573541964,1573715863; close_finance_popup=2019-11-14; cainfo=%7B%22ca_a%22%3A%22-%22%2C%22ca_b%22%3A%22-%22%2C%22ca_s%22%3A%22seo_baidu%22%2C%22ca_n%22%3A%22default%22%2C%22ca_medium%22%3A%22-%22%2C%22ca_term%22%3A%22-%22%2C%22ca_content%22%3A%22-%22%2C%22ca_campaign%22%3A%22-%22%2C%22ca_kw%22%3A%22-%22%2C%22ca_i%22%3A%22-%22%2C%22scode%22%3A%22-%22%2C%22keyword%22%3A%22-%22%2C%22ca_keywordid%22%3A%22-%22%2C%22display_finance_flag%22%3A%22-%22%2C%22platform%22%3A%221%22%2C%22version%22%3A1%2C%22client_ab%22%3A%22-%22%2C%22guid%22%3A%2206ce7520-ebd1-45bc-f41f-a95f2c9b2283%22%2C%22ca_city%22%3A%22wh%22%2C%22sessionid%22%3A%22fefbd4f8-0a06-4e8a-dc49-8856e1a02a07%22%7D; _gl_tracker=%7B%22ca_source%22%3A%22-%22%2C%22ca_name%22%3A%22-%22%2C%22ca_kw%22%3A%22-%22%2C%22ca_id%22%3A%22-%22%2C%22ca_s%22%3A%22self%22%2C%22ca_n%22%3A%22-%22%2C%22ca_i%22%3A%22-%22%2C%22sid%22%3A56473912809%7D; cityDomain=www; preTime=%7B%22last%22%3A1573720945%2C%22this%22%3A1573469364%2C%22pre%22%3A1573469364%7D; Hm_lpvt_936a6d5df3f3d309bda39e92da3dd52f=1573720946; rfnl=https://www.guazi.com/www/chevrolet/i2c-1r18/; antipas=675i0t513a7447M2L9y418Qq869', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36'}# 获取所有二手车详情页URLdef parse_index(): response = requests.get(url=url, headers=headers) tree = etree.HTML(response.text) url_list = tree.xpath('//li/a[@class=\"car-a\"]/@href') # print(len(url_list)) return url_list# 获取二手车详细信息def parse_detail(content): detail_response = requests.get(url=content, headers=headers) tree = etree.HTML(detail_response.text) # 标题 title = tree.xpath('//h2[@class=\"titlebox\"]/text()') # 移除字符串头尾空格 title = [t.strip() for t in title] # 匹配到两个元素,只取其中一个为标题 title = title[:1] # print(title) # 价格 price_old = tree.xpath('//span[@class=\"pricestype\"]/text()') # 移除字符串头尾空格 price_old = [p.strip() for p in price_old] # 加入单位 price_old = [''.join(price_old + ['万'])] # print(price_old) # 新车指导价 price_new = tree.xpath('//span[@class=\"newcarprice\"]/text()') # 移除字符串头尾空格 price_new = [p.strip() for p in price_new] # 对字符串进行切片,只取数字多少万 price_new = ['¥' + price_new[0].split('价')[1]] # print(price_new) # 车主 owner = tree.xpath('//dl/dt/span/text()') owner = [owner[0].replace('车主:', '')] # print(owner) # 上牌时间 spsj = tree.xpath('//li[@class=\"one\"]/div/text()') # print(spsj) # 表显里程 bxlc = tree.xpath('//li[@class=\"two\"]/div/text()') # print(bxlc) # 上牌地 spd = tree.xpath('//li[@class=\"three\"]/div/text()') # 某些二手车没有上牌地,没有的将其赋值为:未知 if len(spd) == 0: spd = ['未知'] # print(spd) # 排放标准 pfbz = tree.xpath('//li[@class=\"four\"]/div/text()') pfbz = pfbz[:1] # print(pfbz) # 变速箱 bsx = tree.xpath('//li[@class=\"five\"]/div/text()') # print(bsx) # 排量 pl = tree.xpath('//li[@class=\"six\"]/div/text()') # print(pl) # 过户次数 ghcs = tree.xpath('//li[@class=\"seven\"]/div/text()') ghcs = [g.strip() for g in ghcs] ghcs = ghcs[:1] # print(ghcs) # 看车地点 kcdd = tree.xpath('//li[@class=\"eight\"]/div/text()') # print(kcdd) # 年检到期 njdq = tree.xpath('//li[@class=\"nine\"]/div/text()') # print(njdq) # 交强险 jqx = tree.xpath('//li[@class=\"ten\"]/div/text()') # print(jqx) # 商业险到期 syxdq = tree.xpath('//li[@class=\"last\"]/div/text()') syxdq = [s.strip() for s in syxdq] syxdq = syxdq[:1] # print(syxdq) # 保存车辆图片 # 获取图片地址 pic_url = tree.xpath('//li[@class=\"js-bigpic\"]/img/@data-src')[0] pic_response = requests.get(pic_url) # 定义图片名称以及保存的文件夹 pic_name = title[0] + '.jpg' dir_name = 'guazi_pic' # 如果没有该文件夹则创建该文件夹 if not os.path.exists(dir_name): os.mkdir(dir_name) # 定义储存路径 pic_path = dir_name + '/' + pic_name with open(pic_path, \"wb\")as f: f.write(pic_response.content) # 将每辆二手车的所有信息合并为一个列表 data = title + price_old + price_new + owner + spsj + bxlc + spd + pfbz + bsx + pl + ghcs + kcdd + njdq + jqx + syxdq return data# 将数据储存到 MongoDBdef save_data(data): client = pymongo.MongoClient(host='localhost', port=27017) db = client.guazi collection = db.esc esc = { '标题': data[0], '二手车价格': data[1], '新车指导价': data[2], '车主': data[3], '上牌时间': data[4], '表显里程': data[5], '上牌地': data[6], '排放标准': data[7], '变速箱': data[8], '排量': data[9], '过户次数': data[10], '看车地点': data[11], '年检到期': data[12], '交强险': data[13], '商业险到期': data[14] } collection.insert_one(esc)if __name__ == '__main__': for i in range(1, 51): num = 0 print('正在爬取第' + str(i) + '页数据...') url = 'https://www.guazi.com/www/buy/o%sc-1/' % i detail_urls = parse_index() for detail_url in detail_urls: car_url = 'https://www.guazi.com' + detail_url car_data = parse_detail(car_url) save_data(car_data) num += 1 print('第' + str(num) + '条数据爬取完毕!') # 在3-10秒之间随机暂停 time.sleep(random.randint(3, 10)) print('第' + str(i) + '页数据爬取完毕!') print('=====================') time.sleep(random.randint(5, 60)) print('所有数据爬取完毕!') 【5x00】数据截图爬取的汽车图片: 储存到 MongoDB 的数据: 数据导出为 CSV 文件: 【6x00】程序不足的地方Cookie 过一段时间就会失效,数据还没爬取完就失效了,导致无法继续爬取;爬取效率不高,可以考虑多线程爬取","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫实战","slug":"Python3-学习笔记/爬虫实战","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫实战/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"瓜子二手车","slug":"瓜子二手车","permalink":"https://www.itrhx.com/tags/瓜子二手车/"}]},{"title":"Python3 爬虫实战 — 58同城武汉出租房【加密字体对抗】","slug":"A58-pyspider-58tongcheng","date":"2019-10-21T13:22:53.980Z","updated":"2019-10-21T13:32:53.413Z","comments":true,"path":"2019/10/21/A58-pyspider-58tongcheng/","link":"","permalink":"https://www.itrhx.com/2019/10/21/A58-pyspider-58tongcheng/","excerpt":"爬取时间:2019-10-21爬取难度:★★★☆☆☆请求链接:https://wh.58.com/chuzu/爬取目标:58同城武汉出租房的所有信息涉及知识:网站加密字体的攻克、请求库 requests、解析库 Beautiful Soup、数据库 MySQL 的操作完整代码:https://github.com/TRHX/Python3-Spider-Practice/tree/master/58tongcheng其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278","text":"爬取时间:2019-10-21爬取难度:★★★☆☆☆请求链接:https://wh.58.com/chuzu/爬取目标:58同城武汉出租房的所有信息涉及知识:网站加密字体的攻克、请求库 requests、解析库 Beautiful Soup、数据库 MySQL 的操作完整代码:https://github.com/TRHX/Python3-Spider-Practice/tree/master/58tongcheng其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278 【1x00】加密字体攻克思路F12 打开调试模板,通过页面分析,可以观察到,网站里面凡是涉及到有数字的地方,都是显示为乱码,这种情况就是字体加密了,那么是通过什么手段实现字体加密的呢? CSS 中有一个 @font-face 规则,它允许为网页指定在线字体,也就是说可以引入自定义字体,这个规则本意是用来消除对电脑字体的依赖,现在不少网站也利用这个规则来实现反爬 右侧可以看到网站用的字体,其他的都是常见的微软雅黑,宋体等,但是有一个特殊的:fangchan-secret ,不难看出这应该就是58同城的自定义字体了 我们通过控制台看到的乱码事实上是由于 unicode 编码导致,查看网页源代码,我们才能看到他真正的编码信息 要攻克加密字体,那么我们肯定要分析他的字体文件了,先想办法得到他的加密字体文件,同样查看源代码,在源代码中搜索 fangchan-secret 的字体信息 选中的蓝色部分就是 base64 编码的加密字体字符串了,我们将其解码成二进制编码,写进 .woff 的字体文件,这个过程可以通过以下代码实现: 1234567891011121314151617import requestsimport base64headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'}url = 'https://wh.58.com/chuzu/'response = requests.get(url=url, headers=headers)# 匹配 base64 编码的加密字体字符串base64_string = response.text.split(\"base64,\")[1].split(\"'\")[0].strip()# 将 base64 编码的字体字符串解码成二进制编码bin_data = base64.decodebytes(base64_string.encode())# 保存为字体文件with open('58font.woff', 'wb') as f: f.write(bin_data) 得到字体文件后,我们可以通过 FontCreator 这个软件来看看字体对应的编码是什么: 观察我们在网页源代码中看到的编码:类似于 &#x9fa4;、&#x9f92; 对比字体文件对应的编码:类似于 uni9FA4、nui9F92 可以看到除了前面三个字符不一样以外,后面的字符都是一样的,只不过英文大小写有所差异 现在我们可能会想到,直接把编码替换成对应的数字不就OK了?然而并没有这么简单 尝试刷新一下网页,可以观察到 base64 编码的加密字体字符串会改变,也就是说编码和数字并不是一一对应的,再次获取几个字体文件,通过对比就可以看出来 可以看到,虽然每次数字对应的编码都不一样,但是编码总是这10个,是不变的,那么编码与数字之间肯定存在某种对应关系,,我们可以将字体文件转换为 xml 文件来观察其中的对应关系,改进原来的代码即可实现转换功能: 123456789101112131415161718192021import requestsimport base64from fontTools.ttLib import TTFontheaders = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'}url = 'https://wh.58.com/chuzu/'response = requests.get(url=url, headers=headers)# 匹配 base64 编码的加密字体字符串base64_string = response.text.split(\"base64,\")[1].split(\"'\")[0].strip()# 将 base64 编码的字体字符串解码成二进制编码bin_data = base64.decodebytes(base64_string.encode())# 保存为字体文件with open('58font.woff', 'wb') as f: f.write(bin_data)# 获取字体文件,将其转换为xml文件font = TTFont('58font.woff')font.saveXML('58font.xml') 打开 58font.xml 文件并分析,在 <cmap> 标签内可以看到熟悉的类似于 0x9476、0x958f 的编码,其后四位字符恰好是网页字体的加密编码,可以看到每一个编码后面都对应了一个 glyph 开头的编码 将其与 58font.woff 文件对比,可以看到 code 为 0x958f 这个编码对应的是数字 3,对应的 name 编码是 glyph00004 我们再次获取一个字体文件作为对比分析 依然是 0x958f 这个编码,两次对应的 name 分别是 glyph00004 和 glyph00007,两次对应的数字分别是 3 和 6,那么结论就来了,每次发送请求,code 对应的 name 会随机发生变化,而 name 对应的数字不会发生变化,glyph00001 对应数字 0、glyph00002 对应数字 1,以此类推 那么以 glyph 开头的编码是如何对应相应的数字的呢?在 xml 文件里面,每个编码都有一个 TTGlyph 的标签,标签里面是一行一行的类似于 x,y 坐标的东西,这个其实就是用来绘制字体的,用 matplotlib 根据坐标画个图,就可以看到是一个数字 此时,我们就知道了编码与数字的对应关系,下一步,我们可以查找 xml 文件里,编码对应的 name 的值,也就是以 glyph 开头的编码,然后返回其对应的数字,再替换掉网页源代码里的编码,就能成功获取到我们需要的信息了! 总结一下攻克加密字体的大致思路: 分析网页,找到对应的加密字体文件 如果引用的加密字体是一个 base64 编码的字符串,则需要转换成二进制并保存到 woff 字体文件中 将字体文件转换成 xml 文件 用 FontCreator 软件观察字体文件,结合 xml 文件,分析其编码与真实字体的关系 搞清楚编码与字体的关系后,想办法将编码替换成正常字体 【2x00】思维导图 【3x00】加密字体处理模块【3x01】获取字体文件并转换为xml文件12345678910111213141516def get_font(page_url, page_num): response = requests.get(url=page_url, headers=headers) # 匹配 base64 编码的加密字体字符串 base64_string = response.text.split(\"base64,\")[1].split(\"'\")[0].strip() # print(base64_string) # 将 base64 编码的字体字符串解码成二进制编码 bin_data = base64.decodebytes(base64_string.encode()) # 保存为字体文件 with open('58font.woff', 'wb') as f: f.write(bin_data) print('第' + str(page_num) + '次访问网页,字体文件保存成功!') # 获取字体文件,将其转换为xml文件 font = TTFont('58font.woff') font.saveXML('58font.xml') print('已成功将字体文件转换为xml文件!') return response.text 由主函数传入要发送请求的 url,利用字符串的 split() 方法,匹配 base64 编码的加密字体字符串,利用 base64 模块的 base64.decodebytes() 方法,将 base64 编码的字体字符串解码成二进制编码并保存为字体文件,利用 FontTools 库,将字体文件转换为 xml 文件 【3x02】将加密字体编码与真实字体进行匹配12345678910111213141516171819202122232425262728293031def find_font(): # 以glyph开头的编码对应的数字 glyph_list = { 'glyph00001': '0', 'glyph00002': '1', 'glyph00003': '2', 'glyph00004': '3', 'glyph00005': '4', 'glyph00006': '5', 'glyph00007': '6', 'glyph00008': '7', 'glyph00009': '8', 'glyph00010': '9' } # 十个加密字体编码 unicode_list = ['0x9476', '0x958f', '0x993c', '0x9a4b', '0x9e3a', '0x9ea3', '0x9f64', '0x9f92', '0x9fa4', '0x9fa5'] num_list = [] # 利用xpath语法匹配xml文件内容 font_data = etree.parse('./58font.xml') for unicode in unicode_list: # 依次循环查找xml文件里code对应的name result = font_data.xpath(\"//cmap//map[@code='{}']/@name\".format(unicode))[0] # print(result) # 循环字典的key,如果code对应的name与字典的key相同,则得到key对应的value for key in glyph_list.keys(): if key == result: num_list.append(glyph_list[key]) print('已成功找到编码所对应的数字!') # print(num_list) # 返回value列表 return num_list 由前面的分析,我们知道 name 的值(即以 glyph 开头的编码)对应的数字是固定的,glyph00001 对应数字 0、glyph00002 对应数字 1,以此类推,所以可以将其构造成为一个字典 glyph_list 同样将十个 code(即类似于 0x9476 的加密字体编码)构造成一个列表 循环查找这十个 code 在 xml 文件里对应的 name 的值,然后将 name 的值与字典文件的 key 值进行对比,如果两者值相同,则获取这个 key 的 value 值,最终得到的列表 num_list,里面的元素就是 unicode_list 列表里面每个加密字体的真实值 【3x03】替换掉网页中所有的加密字体编码12345def replace_font(num, page_response): # 9476 958F 993C 9A4B 9E3A 9EA3 9F64 9F92 9FA4 9FA5 result = page_response.replace('&#x9476;', num[0]).replace('&#x958f;', num[1]).replace('&#x993c;', num[2]).replace('&#x9a4b;', num[3]).replace('&#x9e3a;', num[4]).replace('&#x9ea3;', num[5]).replace('&#x9f64;', num[6]).replace('&#x9f92;', num[7]).replace('&#x9fa4;', num[8]).replace('&#x9fa5;', num[9]) print('已成功将所有加密字体替换!') return result 传入由上一步 find_font() 函数得到的真实字体的列表,利用 replace() 方法,依次将十个加密字体编码替换掉 【4x00】租房信息提取模块1234567891011121314151617181920212223242526272829303132333435363738def parse_pages(pages): num = 0 soup = BeautifulSoup(pages, 'lxml') # 查找到包含所有租房的li标签 all_house = soup.find_all('li', class_='house-cell') for house in all_house: # 标题 title = house.find('a', class_='strongbox').text.strip() # print(title) # 价格 price = house.find('div', class_='money').text.strip() # print(price) # 户型和面积 layout = house.find('p', class_='room').text.replace(' ', '') # print(layout) # 楼盘和地址 address = house.find('p', class_='infor').text.replace(' ', '').replace('\\n', '') # print(address) # 如果存在经纪人 if house.find('div', class_='jjr'): agent = house.find('div', class_='jjr').text.replace(' ', '').replace('\\n', '') # 如果存在品牌公寓 elif house.find('p', class_='gongyu'): agent = house.find('p', class_='gongyu').text.replace(' ', '').replace('\\n', '') # 如果存在个人房源 else: agent = house.find('p', class_='geren').text.replace(' ', '').replace('\\n', '') # print(agent) data = [title, price, layout, address, agent] save_to_mysql(data) num += 1 print('第' + str(num) + '条数据爬取完毕,暂停3秒!') time.sleep(3) 利用 BeautifulSoup 解析库很容易提取到相关信息,这里要注意的是,租房信息来源分为三种:经纪人、品牌公寓和个人房源,这三个的元素节点也不一样,因此匹配的时候要注意 【5x00】MySQL数据储存模块【5x01】创建MySQL数据库的表123456def create_mysql_table(): db = pymysql.connect(host='localhost', user='root', password='000000', port=3306, db='58tc_spiders') cursor = db.cursor() sql = 'CREATE TABLE IF NOT EXISTS 58tc_data (title VARCHAR(255) NOT NULL, price VARCHAR(255) NOT NULL, layout VARCHAR(255) NOT NULL, address VARCHAR(255) NOT NULL, agent VARCHAR(255) NOT NULL)' cursor.execute(sql) db.close() 首先指定数据库为 58tc_spiders,需要事先使用 MySQL 语句创建,也可以通过 MySQL Workbench 手动创建 然后使用 SQL 语句创建 一个表:58tc_data,表中包含 title、price、layout、address、agent 五个字段,类型都为 varchar 此创建表的操作也可以事先手动创建,手动创建后就不需要此函数了 【5x02】将数据储存到MySQL数据库12345678910def save_to_mysql(data): db = pymysql.connect(host='localhost', user='root', password='000000', port=3306, db='58tc_spiders') cursor = db.cursor() sql = 'INSERT INTO 58tc_data(title, price, layout, address, agent) values(%s, %s, %s, %s, %s)' try: cursor.execute(sql, (data[0], data[1], data[2], data[3], data[4])) db.commit() except: db.rollback() db.close() commit() 方法的作用是实现数据插入,是真正将语句提交到数据库执行的方法,使用 try except 语句实现异常处理,如果执行失败,则调用 rollback() 方法执行数据回滚,保证原数据不被破坏 【6x00】完整代码123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160# =============================================# --*-- coding: utf-8 --*--# @Time : 2019-10-21# @Author : TRHX# @Blog : www.itrhx.com# @CSDN : https://blog.csdn.net/qq_36759224# @FileName: 58tongcheng.py# @Software: PyCharm# =============================================import requestsimport timeimport randomimport base64import pymysqlfrom lxml import etreefrom bs4 import BeautifulSoupfrom fontTools.ttLib import TTFontheaders = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'}# 获取字体文件并转换为xml文件def get_font(page_url, page_num): response = requests.get(url=page_url, headers=headers) # 匹配 base64 编码的加密字体字符串 base64_string = response.text.split(\"base64,\")[1].split(\"'\")[0].strip() # print(base64_string) # 将 base64 编码的字体字符串解码成二进制编码 bin_data = base64.decodebytes(base64_string.encode()) # 保存为字体文件 with open('58font.woff', 'wb') as f: f.write(bin_data) print('第' + str(page_num) + '次访问网页,字体文件保存成功!') # 获取字体文件,将其转换为xml文件 font = TTFont('58font.woff') font.saveXML('58font.xml') print('已成功将字体文件转换为xml文件!') return response.text# 将加密字体编码与真实字体进行匹配def find_font(): # 以glyph开头的编码对应的数字 glyph_list = { 'glyph00001': '0', 'glyph00002': '1', 'glyph00003': '2', 'glyph00004': '3', 'glyph00005': '4', 'glyph00006': '5', 'glyph00007': '6', 'glyph00008': '7', 'glyph00009': '8', 'glyph00010': '9' } # 十个加密字体编码 unicode_list = ['0x9476', '0x958f', '0x993c', '0x9a4b', '0x9e3a', '0x9ea3', '0x9f64', '0x9f92', '0x9fa4', '0x9fa5'] num_list = [] # 利用xpath语法匹配xml文件内容 font_data = etree.parse('./58font.xml') for unicode in unicode_list: # 依次循环查找xml文件里code对应的name result = font_data.xpath(\"//cmap//map[@code='{}']/@name\".format(unicode))[0] # print(result) # 循环字典的key,如果code对应的name与字典的key相同,则得到key对应的value for key in glyph_list.keys(): if key == result: num_list.append(glyph_list[key]) print('已成功找到编码所对应的数字!') # print(num_list) # 返回value列表 return num_list# 替换掉网页中所有的加密字体编码def replace_font(num, page_response): # 9476 958F 993C 9A4B 9E3A 9EA3 9F64 9F92 9FA4 9FA5 result = page_response.replace('&#x9476;', num[0]).replace('&#x958f;', num[1]).replace('&#x993c;', num[2]).replace('&#x9a4b;', num[3]).replace('&#x9e3a;', num[4]).replace('&#x9ea3;', num[5]).replace('&#x9f64;', num[6]).replace('&#x9f92;', num[7]).replace('&#x9fa4;', num[8]).replace('&#x9fa5;', num[9]) print('已成功将所有加密字体替换!') return result# 提取租房信息def parse_pages(pages): num = 0 soup = BeautifulSoup(pages, 'lxml') # 查找到包含所有租房的li标签 all_house = soup.find_all('li', class_='house-cell') for house in all_house: # 标题 title = house.find('a', class_='strongbox').text.strip() # print(title) # 价格 price = house.find('div', class_='money').text.strip() # print(price) # 户型和面积 layout = house.find('p', class_='room').text.replace(' ', '') # print(layout) # 楼盘和地址 address = house.find('p', class_='infor').text.replace(' ', '').replace('\\n', '') # print(address) # 如果存在经纪人 if house.find('div', class_='jjr'): agent = house.find('div', class_='jjr').text.replace(' ', '').replace('\\n', '') # 如果存在品牌公寓 elif house.find('p', class_='gongyu'): agent = house.find('p', class_='gongyu').text.replace(' ', '').replace('\\n', '') # 如果存在个人房源 else: agent = house.find('p', class_='geren').text.replace(' ', '').replace('\\n', '') # print(agent) data = [title, price, layout, address, agent] save_to_mysql(data) num += 1 print('第' + str(num) + '条数据爬取完毕,暂停3秒!') time.sleep(3)# 创建MySQL数据库的表:58tc_datadef create_mysql_table(): db = pymysql.connect(host='localhost', user='root', password='000000', port=3306, db='58tc_spiders') cursor = db.cursor() sql = 'CREATE TABLE IF NOT EXISTS 58tc_data (title VARCHAR(255) NOT NULL, price VARCHAR(255) NOT NULL, layout VARCHAR(255) NOT NULL, address VARCHAR(255) NOT NULL, agent VARCHAR(255) NOT NULL)' cursor.execute(sql) db.close()# 将数据储存到MySQL数据库def save_to_mysql(data): db = pymysql.connect(host='localhost', user='root', password='000000', port=3306, db='58tc_spiders') cursor = db.cursor() sql = 'INSERT INTO 58tc_data(title, price, layout, address, agent) values(%s, %s, %s, %s, %s)' try: cursor.execute(sql, (data[0], data[1], data[2], data[3], data[4])) db.commit() except: db.rollback() db.close()if __name__ == '__main__': create_mysql_table() print('MySQL表58tc_data创建成功!') for i in range(1, 71): url = 'https://wh.58.com/chuzu/pn' + str(i) + '/' response = get_font(url, i) num_list = find_font() pro_pages = replace_font(num_list, response) parse_pages(pro_pages) print('第' + str(i) + '页数据爬取完毕!') time.sleep(random.randint(3, 60)) print('所有数据爬取完毕!') 【7x00】数据截图","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫实战","slug":"Python3-学习笔记/爬虫实战","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫实战/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"58同城","slug":"58同城","permalink":"https://www.itrhx.com/tags/58同城/"}]},{"title":"Python3 爬虫实战 — 模拟登陆12306【点触验证码对抗】","slug":"A57-pyspider-12306-login","date":"2019-10-21T08:41:50.349Z","updated":"2019-10-21T13:33:47.617Z","comments":true,"path":"2019/10/21/A57-pyspider-12306-login/","link":"","permalink":"https://www.itrhx.com/2019/10/21/A57-pyspider-12306-login/","excerpt":"登陆时间:2019-10-21实现难度:★★★☆☆☆请求链接:https://kyfw.12306.cn/otn/resources/login.html实现目标:模拟登陆中国铁路12306,攻克点触验证码涉及知识:点触验证码的攻克、自动化测试工具 Selenium 的使用、对接在线打码平台完整代码:https://github.com/TRHX/Python3-Spider-Practice/tree/master/12306-login其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278","text":"登陆时间:2019-10-21实现难度:★★★☆☆☆请求链接:https://kyfw.12306.cn/otn/resources/login.html实现目标:模拟登陆中国铁路12306,攻克点触验证码涉及知识:点触验证码的攻克、自动化测试工具 Selenium 的使用、对接在线打码平台完整代码:https://github.com/TRHX/Python3-Spider-Practice/tree/master/12306-login其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278 【1x00】思维导图 利用自动化测试工具 Selenium 直接模拟人的行为方式来完成验证 发送请求,出现验证码后,剪裁并保存验证码图片 选择在线打码平台,获取其API,以字节流格式发送图片 打码平台人工识别验证码,返回验证码的坐标信息 解析返回的坐标信息,模拟点击验证码,完成验证后点击登陆 【2x00】打码平台选择关于打码平台:在线打码平台全部都是人工在线识别,准确率非常高,原理就是先将验证码图片提交给平台,平台会返回识别结果在图片中的坐标位置,然后我们再解析坐标模拟点击即可,常见的打码平台有超级鹰、云打码等,打码平台是收费的,拿超级鹰来说,1元 = 1000题分,识别一次验证码将花费一定的题分,不同类型验证码需要的题分不同,验证码越复杂所需题分越高,比如 7 位中文汉字需要 70 题分,常见 4 ~ 6 位英文数字只要 10 题分,其他打码平台价格也都差不多,本次实战使用超级鹰打码平台 使用打码平台:在超级鹰打码平台注册账号,官网:http://www.chaojiying.com/ ,充值一块钱得到 1000 题分,在用户中心里面申请一个软件 ID ,在价格体系里面确定验证码的类型,先观察 12306 官网,发现验证码是要我们点击所有满足条件的图片,一般有 1 至 4 张图片满足要求,由此可确定在超级鹰打码平台的验证码类型为 9004(坐标多选,返回1~4个坐标,如:x1,y1|x2,y2|x3,y3), 然后在开发文档里面获取其 Python API,下载下来以备后用 【3x00】初始化模块【3x01】初始化函数1234567891011121314151617181920212223242526# 12306账号密码USERNAME = '155********'PASSWORD = '***********'# 超级鹰打码平台账号密码CHAOJIYING_USERNAME = '*******'CHAOJIYING_PASSWORD = '*******'# 超级鹰打码平台软件IDCHAOJIYING_SOFT_ID = '********'# 验证码类型CHAOJIYING_KIND = '9004'class CrackTouClick(): def __init__(self): self.url = 'https://kyfw.12306.cn/otn/resources/login.html' # path是谷歌浏览器驱动的目录,如果已经将目录添加到系统变量,则不用设置此路径 path = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe' chrome_options = Options() chrome_options.add_argument('--start-maximized') self.browser = webdriver.Chrome(executable_path=path, chrome_options=chrome_options) self.wait = WebDriverWait(self.browser, 20) self.username = USERNAME self.password = PASSWORD self.chaojiying = ChaojiyingClient(CHAOJIYING_USERNAME, CHAOJIYING_PASSWORD, CHAOJIYING_SOFT_ID) 定义 12306 账号(USERNAME)、密码(PASSWORD)、超级鹰用户名(CHAOJIYING_USERNAME)、超级鹰登录密码(CHAOJIYING_PASSWORD)、超级鹰软件 ID(CHAOJIYING_SOFT_ID)、验证码类型(CHAOJIYING_KIND),登录页面 url ,谷歌浏览器驱动的目录(path),浏览器启动参数等,将超级鹰账号密码等相关参数传递给超级鹰 API 【3x02】账号密码输入函数12345678910111213def get_input_element(self): # 登录页面发送请求 self.browser.get(self.url) # 登录页面默认是扫码登录,所以首先要点击账号登录 login = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.login-hd-account'))) login.click() time.sleep(3) # 查找到账号密码输入位置的元素 username = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'input#J-userName'))) password = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'input#J-password'))) # 输入账号密码 username.send_keys(self.username) password.send_keys(self.password) 分析页面可知,登陆页面默认出现的是扫描二维码登陆,所以要先点击账号登录,找到该 CSS 元素为 login-hd-account,调用 click() 方法实现模拟点击,此时出现账号密码输入框,同样找到其 ID 分别为 J-userName 和 J-password,调用 send_keys() 方法输入账号密码 【4x00】验证码处理模块1234567891011121314151617181920212223242526def crack(self): # 调用账号密码输入函数 self.get_input_element() # 调用验证码图片剪裁函数 image = self.get_touclick_image() bytes_array = BytesIO() image.save(bytes_array, format='PNG') # 利用超级鹰打码平台的 API PostPic() 方法把图片发送给超级鹰后台,发送的图像是字节流格式,返回的结果是一个JSON result = self.chaojiying.PostPic(bytes_array.getvalue(), CHAOJIYING_KIND) print(result) # 调用验证码坐标解析函数 locations = self.get_points(result) # 调用模拟点击验证码函数 self.touch_click_words(locations) # 调用模拟点击登录函数 self.login() try: # 查找是否出现用户的姓名,若出现表示登录成功 success = self.wait.until(EC.text_to_be_present_in_element((By.CSS_SELECTOR, '.welcome-name'), '谭先生')) print(success) cc = self.browser.find_element(By.CSS_SELECTOR, '.welcome-name') print('用户' + cc.text + '登录成功') # 若没有出现表示登录失败,继续重试,超级鹰会返回本次识别的分值 except TimeoutException: self.chaojiying.ReportError(result['pic_id']) self.crack() crack() 为验证码处理模块的主函数 调用账号密码输入函数 get_input_element(),等待账号密码输入完毕 调用验证码图片剪裁函数 get_touclick_image(),得到验证码图片 利用超级鹰打码平台的 API PostPic() 方法把图片发送给超级鹰后台,发送的图像是字节流格式,返回的结果是一个JSON,如果识别成功,典型的返回结果类似于: 12{'err_no': 0, 'err_str': 'OK', 'pic_id': '6002001380949200001', 'pic_str': '132,127|56,77', 'md5': '1f8e1d4bef8b11484cb1f1f34299865b'} 其中,pic_str 就是识别的文字的坐标,是以字符串形式返回的,每个坐标都以 | 分隔 调用 get_points() 函数解析超级鹰识别结果 调用 touch_click_words() 函数对符合要求的图片进行点击 调用模拟点击登录函数 login(),点击登陆按钮模拟登陆 使用 try-except 语句判断是否出现了用户信息,判断依据是是否有用户姓名的出现,出现的姓名和实际姓名一致则登录成功,如果失败了就重试,超级鹰会返回该分值 【4x01】验证码图片剪裁函数1234567891011121314def get_touclick_image(self, name='12306.png'): # 获取验证码的位置 element = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.login-pwd-code'))) time.sleep(3) location = element.location size = element.size top, bottom, left, right = location['y'], location['y'] + size['height'], location['x'], location['x'] + size['width'] # 先对整个页面截图 screenshot = self.browser.get_screenshot_as_png() screenshot = Image.open(BytesIO(screenshot)) # 根据验证码坐标信息,剪裁出验证码图片 captcha = screenshot.crop((left, top, right, bottom)) captcha.save(name) return captcha 首先查找到验证码的坐标信息,先对整个页面截图,然后根据验证码坐标信息,剪裁出验证码图片 location 属性可以返回该图片对象在浏览器中的位置,坐标轴是以屏幕左上角为原点,x 轴向右递增,y 轴向下递增,size 属性可以返回该图片对象的高度和宽度,由此可以得到验证码的位置信息 【4x02】验证码坐标解析函数123456def get_points(self, captcha_result): # 超级鹰识别结果以字符串形式返回,每个坐标都以|分隔 groups = captcha_result.get('pic_str').split('|') # 将坐标信息变成列表的形式 locations = [[int(number) for number in group.split(',')] for group in groups] return locations get_points() 方法将超级鹰的验证码识别结果变成列表的形式 【4x03】模拟点击验证码函数123456def touch_click_words(self, locations): element = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.login-pwd-code'))) # 循环点击正确验证码的坐标 for location in locations: print(location) ActionChains(self.browser).move_to_element_with_offset(element, location[0], location[1]).click().perform() 循环提取正确的验证码坐标信息,依次点击验证码 【5x00】登录模块123def login(self): submit = self.wait.until(EC.element_to_be_clickable((By.ID, 'J-login'))) submit.click() 分析页面,找到登陆按钮的 ID 为 J-login,调用 click() 方法模拟点击按钮实现登录 【6x00】完整代码【6x01】12306.py123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133# =============================================# --*-- coding: utf-8 --*--# @Time : 2019-10-21# @Author : TRHX# @Blog : www.itrhx.com# @CSDN : https://blog.csdn.net/qq_36759224# @FileName: 12306.py# @Software: PyCharm# =============================================import timefrom io import BytesIOfrom PIL import Imagefrom selenium import webdriverfrom selenium.webdriver.chrome.options import Optionsfrom selenium.webdriver import ActionChainsfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECfrom chaojiying import ChaojiyingClientfrom selenium.common.exceptions import TimeoutException# 12306账号密码USERNAME = '155********'PASSWORD = '***********'# 超级鹰打码平台账号密码CHAOJIYING_USERNAME = '********'CHAOJIYING_PASSWORD = '********'# 超级鹰打码平台软件IDCHAOJIYING_SOFT_ID = '******'# 验证码类型CHAOJIYING_KIND = '9004'class CrackTouClick(): def __init__(self): self.url = 'https://kyfw.12306.cn/otn/resources/login.html' # path是谷歌浏览器驱动的目录,如果已经将目录添加到系统变量,则不用设置此路径 path = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe' chrome_options = Options() chrome_options.add_argument('--start-maximized') self.browser = webdriver.Chrome(executable_path=path, chrome_options=chrome_options) self.wait = WebDriverWait(self.browser, 20) self.username = USERNAME self.password = PASSWORD self.chaojiying = ChaojiyingClient(CHAOJIYING_USERNAME, CHAOJIYING_PASSWORD, CHAOJIYING_SOFT_ID) def crack(self): # 调用账号密码输入函数 self.get_input_element() # 调用验证码图片剪裁函数 image = self.get_touclick_image() bytes_array = BytesIO() image.save(bytes_array, format='PNG') # 利用超级鹰打码平台的 API PostPic() 方法把图片发送给超级鹰后台,发送的图像是字节流格式,返回的结果是一个JSON result = self.chaojiying.PostPic(bytes_array.getvalue(), CHAOJIYING_KIND) print(result) # 调用验证码坐标解析函数 locations = self.get_points(result) # 调用模拟点击验证码函数 self.touch_click_words(locations) # 调用模拟点击登录函数 self.login() try: # 查找是否出现用户的姓名,若出现表示登录成功 success = self.wait.until(EC.text_to_be_present_in_element((By.CSS_SELECTOR, '.welcome-name'), '谭先生')) print(success) cc = self.browser.find_element(By.CSS_SELECTOR, '.welcome-name') print('用户' + cc.text + '登录成功') # 若没有出现表示登录失败,继续重试,超级鹰会返回本次识别的分值 except TimeoutException: self.chaojiying.ReportError(result['pic_id']) self.crack() # 账号密码输入函数 def get_input_element(self): # 登录页面发送请求 self.browser.get(self.url) # 登录页面默认是扫码登录,所以首先要点击账号登录 login = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.login-hd-account'))) login.click() time.sleep(3) # 查找到账号密码输入位置的元素 username = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'input#J-userName'))) password = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'input#J-password'))) # 输入账号密码 username.send_keys(self.username) password.send_keys(self.password) # 验证码图片剪裁函数 def get_touclick_image(self, name='12306.png'): # 获取验证码的位置 element = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.login-pwd-code'))) time.sleep(3) location = element.location size = element.size top, bottom, left, right = location['y'], location['y'] + size['height'], location['x'], location['x'] + size[ 'width'] # 先对整个页面截图 screenshot = self.browser.get_screenshot_as_png() screenshot = Image.open(BytesIO(screenshot)) # 根据验证码坐标信息,剪裁出验证码图片 captcha = screenshot.crop((left, top, right, bottom)) captcha.save(name) return captcha # 验证码坐标解析函数,分析超级鹰返回的坐标 def get_points(self, captcha_result): # 超级鹰识别结果以字符串形式返回,每个坐标都以|分隔 groups = captcha_result.get('pic_str').split('|') # 将坐标信息变成列表的形式 locations = [[int(number) for number in group.split(',')] for group in groups] return locations # 模拟点击验证码函数 def touch_click_words(self, locations): element = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.login-pwd-code'))) # 循环点击正确验证码的坐标 for location in locations: print(location) ActionChains(self.browser).move_to_element_with_offset(element, location[0], location[1]).click().perform() # 模拟点击登录函数 def login(self): submit = self.wait.until(EC.element_to_be_clickable((By.ID, 'J-login'))) submit.click()if __name__ == '__main__': crack = CrackTouClick() crack.crack() 【6x02】chaojiying.py12345678910111213141516171819202122232425262728293031323334353637383940414243import requestsfrom hashlib import md5class ChaojiyingClient(object): def __init__(self, username, password, soft_id): self.username = username password = password.encode('utf8') self.password = md5(password).hexdigest() self.soft_id = soft_id self.base_params = { 'user': self.username, 'pass2': self.password, 'softid': self.soft_id, } self.headers = { 'Connection': 'Keep-Alive', 'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)', } def PostPic(self, im, codetype): \"\"\" im: 图片字节 codetype: 题目类型 参考 http://www.chaojiying.com/price.html \"\"\" params = { 'codetype': codetype, } params.update(self.base_params) files = {'userfile': ('ccc.jpg', im)} r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, files=files, headers=self.headers) return r.json() def ReportError(self, im_id): \"\"\" im_id:报错题目的图片ID \"\"\" params = { 'id': im_id, } params.update(self.base_params) r = requests.post('http://upload.chaojiying.net/Upload/ReportError.php', data=params, headers=self.headers) return r.json() 【7x00】效果实现动图最终实现效果图:(关键信息已经过打码处理)","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫实战","slug":"Python3-学习笔记/爬虫实战","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫实战/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"12306","slug":"12306","permalink":"https://www.itrhx.com/tags/12306/"}]},{"title":"Python3 爬虫实战 — 模拟登陆哔哩哔哩【滑动验证码对抗】","slug":"A56-pyspider-bilibili-login","date":"2019-10-21T04:26:46.838Z","updated":"2019-10-21T13:33:57.637Z","comments":true,"path":"2019/10/21/A56-pyspider-bilibili-login/","link":"","permalink":"https://www.itrhx.com/2019/10/21/A56-pyspider-bilibili-login/","excerpt":"登陆时间:2019-10-21实现难度:★★★☆☆☆请求链接:https://passport.bilibili.com/login实现目标:模拟登陆哔哩哔哩,攻克滑动验证码涉及知识:滑动验证码的攻克、自动化测试工具 Selenium 的使用完整代码:https://github.com/TRHX/Python3-Spider-Practice/tree/master/bilibili-login其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278","text":"登陆时间:2019-10-21实现难度:★★★☆☆☆请求链接:https://passport.bilibili.com/login实现目标:模拟登陆哔哩哔哩,攻克滑动验证码涉及知识:滑动验证码的攻克、自动化测试工具 Selenium 的使用完整代码:https://github.com/TRHX/Python3-Spider-Practice/tree/master/bilibili-login其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278 【1x00】思维导图 利用自动化测试工具 Selenium 直接模拟人的行为方式来完成验证 分析页面,想办法找到滑动验证码的完整图片、带有缺口的图片和需要滑动的图片 对比原始的图片和带缺口的图片的像素,像素不同的地方就是缺口位置 计算出滑块缺口的位置,得到所需要滑动的距离 拖拽时要模仿人的行为,由于有个对准过程,所以要构造先快后慢的运动轨迹 最后利用 Selenium 进行对滑块的拖拽 【2x00】登陆模块【2x01】初始化函数12345678910111213def init(): global url, browser, username, password, wait url = 'https://passport.bilibili.com/login' # path是谷歌浏览器驱动的目录,如果已经将目录添加到系统变量,则不用设置此路径 path = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe' chrome_options = Options() chrome_options.add_argument('--start-maximized') browser = webdriver.Chrome(executable_path=path, chrome_options=chrome_options) # 你的哔哩哔哩用户名 username = '155********' # 你的哔哩哔哩登陆密码 password = '***********' wait = WebDriverWait(browser, 20) global 关键字定义了发起请求的url、用户名、密码等全局变量,随后是登录页面url、谷歌浏览器驱动的目录path、实例化 Chrome 浏览器、设置浏览器分辨率最大化、用户名、密码、WebDriverWait() 方法设置等待超时 【2x02】登陆函数12345678910111213141516def login(): browser.get(url) # 获取用户名输入框 user = wait.until(EC.presence_of_element_located((By.ID, 'login-username'))) # 获取密码输入框 passwd = wait.until(EC.presence_of_element_located((By.ID, 'login-passwd'))) # 输入用户名 user.send_keys(username) # 输入密码 passwd.send_keys(password) # 获取登录按钮 login_btn = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'a.btn.btn-login'))) # 随机暂停几秒 time.sleep(random.random() * 3) # 点击登陆按钮 login_btn.click() 等待用户名输入框和密码输入框对应的 ID 节点加载出来 获取这两个节点,用户名输入框 id="login-username",密码输入框 id="login-passwd" 调用 send_keys() 方法输入用户名和密码 获取登录按钮 class="btn btn-login" 随机产生一个数并将其扩大三倍作为暂停时间 最后调用 click() 方法实现登录按钮的点击 【3x00】验证码处理模块【3x01】验证码元素查找函数12345678910111213141516171819202122def find_element(): # 获取带有缺口的图片 c_background = wait.until( EC.presence_of_element_located((By.CSS_SELECTOR, 'canvas.geetest_canvas_bg.geetest_absolute'))) # 获取需要滑动的图片 c_slice = wait.until( EC.presence_of_element_located((By.CSS_SELECTOR, 'canvas.geetest_canvas_slice.geetest_absolute'))) # 获取完整的图片 c_full_bg = wait.until( EC.presence_of_element_located((By.CSS_SELECTOR, 'canvas.geetest_canvas_fullbg.geetest_fade.geetest_absolute'))) # 隐藏需要滑动的图片 hide_element(c_slice) # 保存带有缺口的图片 save_screenshot(c_background, 'back') # 显示需要滑动的图片 show_element(c_slice) # 保存需要滑动的图片 save_screenshot(c_slice, 'slice') # 显示完整的图片 show_element(c_full_bg) # 保存完整的图片 save_screenshot(c_full_bg, 'full') 获取验证码的三张图片,分别是完整的图片、带有缺口的图片和需要滑动的图片 分析页面代码,三张图片是由 3 个 canvas 组成,3 个 canvas 元素包含 CSS display 属性,display:block 为可见,display:none 为不可见,在分别获取三张图片时要将其他两张图片设置为 display:none,这样做才能单独提取到每张图片 定位三张图片的 class 分别为:带有缺口的图片(c_background):geetest_canvas_bg geetest_absolute、需要滑动的图片(c_slice):geetest_canvas_slice geetest_absolute、完整图片(c_full_bg):geetest_canvas_fullbg geetest_fade geetest_absolute 最后传值给 save_screenshot() 函数,进一步对验证码进行处理 【3x02】元素可见性设置函数12345678# 设置元素不可见def hide_element(element): browser.execute_script(\"arguments[0].style=arguments[1]\", element, \"display: none;\")# 设置元素可见def show_element(element): browser.execute_script(\"arguments[0].style=arguments[1]\", element, \"display: block;\") 【3x03】验证码截图函数123456789101112131415161718192021222324def save_screenshot(obj, name): try: # 首先对出现验证码后的整个页面进行截图保存 pic_url = browser.save_screenshot('.\\\\bilibili.png') print(\"%s:截图成功!\" % pic_url) # 计算传入的obj,也就是三张图片的位置信息 left = obj.location['x'] top = obj.location['y'] right = left + obj.size['width'] bottom = top + obj.size['height'] # 打印输出一下每一张图的位置信息 print('图:' + name) print('Left %s' % left) print('Top %s' % top) print('Right %s' % right) print('Bottom %s' % bottom) print('') # 在整个页面截图的基础上,根据位置信息,分别剪裁出三张验证码图片并保存 im = Image.open('.\\\\bilibili.png') im = im.crop((left, top, right, bottom)) file_name = 'bili_' + name + '.png' im.save(file_name) except BaseException as msg: print(\"%s:截图失败!\" % msg) location 属性可以返回该图片对象在浏览器中的位置,坐标轴是以屏幕左上角为原点,x轴向右递增,y轴向下递增 size 属性可以返回该图片对象的高度和宽度,由此可以得到验证码的位置信息 首先调用 save_screenshot() 属性对整个页面截图并保存 然后向 crop() 方法传入验证码的位置信息,由位置信息再对验证码进行剪裁并保存 【4x00】验证码滑动模块【4x01】滑动主函数123456def slide(): distance = get_distance(Image.open('.\\\\bili_back.png'), Image.open('.\\\\bili_full.png')) print('计算偏移量为:%s Px' % distance) trace = get_trace(distance - 5) move_to_gap(trace) time.sleep(3) 向 get_distance() 函数传入完整的图片和缺口图片,计算滑块需要滑动的距离,再把距离信息传入 get_trace() 函数,构造滑块的移动轨迹,最后根据轨迹信息调用 move_to_gap() 函数移动滑块完成验证 【4x02】缺口位置寻找函数123456789101112def is_pixel_equal(bg_image, fullbg_image, x, y): # 获取两张图片对应像素点的RGB数据 bg_pixel = bg_image.load()[x, y] fullbg_pixel = fullbg_image.load()[x, y] # 设定一个阈值 threshold = 60 # 比较两张图 RGB 的绝对值是否均小于定义的阈值 if (abs(bg_pixel[0] - fullbg_pixel[0] < threshold) and abs(bg_pixel[1] - fullbg_pixel[1] < threshold) and abs( bg_pixel[2] - fullbg_pixel[2] < threshold)): return True else: return False 将完整图片和缺口图片两个对象分别赋值给变量 bg_image 和 fullbg_image,接下来对比图片获取缺口。遍历图片的每个坐标点,获取两张图片对应像素点的 RGB 数据,判断像素的各个颜色之差,abs() 用于取绝对值,比较两张图 RGB 的绝对值是否均小于定义的阈值 threshold,如果绝对值均在阈值之内,则代表像素点相同,继续遍历,否则代表不相同的像素点,即缺口的位置 【4x03】计算滑块移动距离函数123456789def get_distance(bg_image, fullbg_image): # 滑块的初始位置 distance = 60 # 遍历两张图片的每个像素 for i in range(distance, fullbg_image.size[0]): for j in range(fullbg_image.size[1]): # 调用缺口位置寻找函数 if not is_pixel_equal(fullbg_image, bg_image, i, j): return i get_distance() 方法即获取缺口位置的方法,此方法的参数是两张图片,一张为完整的图片,另一张为带缺口的图片,distance 为滑块的初始位置,遍历两张图片的每个像素,利用 is_pixel_equal() 缺口位置寻找函数判断两张图片同一位置的像素是否相同,若不相同则返回该点的值 【4x04】构造移动轨迹函数1234567891011121314151617181920def get_trace(distance): trace = [] # 设置加速距离为总距离的4/5 faster_distance = distance * (4 / 5) # 设置初始位置、初始速度、时间间隔 start, v0, t = 0, 0, 0.1 while start < distance: if start < faster_distance: a = 10 else: a = -10 # 位移 move = v0 * t + 1 / 2 * a * t * t # 当前时刻的速度 v = v0 + a * t v0 = v start += move trace.append(round(move)) # trace 记录了每个时间间隔移动了多少位移 return trace get_trace() 方法传入的参数为移动的总距离,返回的是运动轨迹,运动轨迹用 trace 表示,它是一个列表,列表的每个元素代表每次移动多少距离,利用 Selenium 进行对滑块的拖拽时要模仿人的行为,由于有个对准过程,所以是先快后慢,匀速移动、随机速度移动都不会成功,因此要设置一个加速和减速的距离,这里设置加速距离 faster_distance 是总距离 distance 的4/5倍,滑块滑动的加速度用 a 来表示,当前速度用 v 表示,初速度用 v0 表示,位移用 move 表示,所需时间用 t 表示,它们之间满足以下关系: 12move = v0 * t + 0.5 * a * t * t v = v0 + a * t 设置初始位置、初始速度、时间间隔分别为0, 0, 0.1,加速阶段和减速阶段的加速度分别设置为10和-10,直到运动轨迹达到总距离时,循环终止,最后得到的 trace 记录了每个时间间隔移动了多少位移,这样滑块的运动轨迹就得到了 【4x05】模拟拖动函数123456789101112def move_to_gap(trace): # 获取滑动按钮 slider = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'div.geetest_slider_button'))) # 点击并拖动滑块 ActionChains(browser).click_and_hold(slider).perform() # 遍历运动轨迹获取每小段位移距离 for x in trace: # 移动此位移 ActionChains(browser).move_by_offset(xoffset=x, yoffset=0).perform() time.sleep(0.5) # 释放鼠标 ActionChains(browser).release().perform() 传入的参数为运动轨迹,首先查找到滑动按钮,然后调用 ActionChains 的 click_and_hold() 方法按住拖动底部滑块,perform() 方法用于执行,遍历运动轨迹获取每小段位移距离,调用 move_by_offset() 方法移动此位移,最后调用 release() 方法松开鼠标即可 【5x00】完整代码123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197# =============================================# --*-- coding: utf-8 --*--# @Time : 2019-10-21# @Author : TRHX# @Blog : www.itrhx.com# @CSDN : https://blog.csdn.net/qq_36759224# @FileName: bilibili.py# @Software: PyCharm# =============================================from selenium import webdriverfrom selenium.webdriver.chrome.options import Optionsfrom selenium.webdriver.support.wait import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECfrom selenium.webdriver.common.by import Byfrom selenium.webdriver import ActionChainsimport timeimport randomfrom PIL import Image# 初始化函数def init(): global url, browser, username, password, wait url = 'https://passport.bilibili.com/login' # path是谷歌浏览器驱动的目录,如果已经将目录添加到系统变量,则不用设置此路径 path = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe' chrome_options = Options() chrome_options.add_argument('--start-maximized') browser = webdriver.Chrome(executable_path=path, chrome_options=chrome_options) # 你的哔哩哔哩用户名 username = '155********' # 你的哔哩哔哩登录密码 password = '***********' wait = WebDriverWait(browser, 20)# 登录函数def login(): browser.get(url) # 获取用户名输入框 user = wait.until(EC.presence_of_element_located((By.ID, 'login-username'))) # 获取密码输入框 passwd = wait.until(EC.presence_of_element_located((By.ID, 'login-passwd'))) # 输入用户名 user.send_keys(username) # 输入密码 passwd.send_keys(password) # 获取登录按钮 login_btn = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'a.btn.btn-login'))) # 随机暂停几秒 time.sleep(random.random() * 3) # 点击登陆按钮 login_btn.click()# 验证码元素查找函数def find_element(): # 获取带有缺口的图片 c_background = wait.until( EC.presence_of_element_located((By.CSS_SELECTOR, 'canvas.geetest_canvas_bg.geetest_absolute'))) # 获取需要滑动的图片 c_slice = wait.until( EC.presence_of_element_located((By.CSS_SELECTOR, 'canvas.geetest_canvas_slice.geetest_absolute'))) # 获取完整的图片 c_full_bg = wait.until( EC.presence_of_element_located((By.CSS_SELECTOR, 'canvas.geetest_canvas_fullbg.geetest_fade.geetest_absolute'))) # 隐藏需要滑动的图片 hide_element(c_slice) # 保存带有缺口的图片 save_screenshot(c_background, 'back') # 显示需要滑动的图片 show_element(c_slice) # 保存需要滑动的图片 save_screenshot(c_slice, 'slice') # 显示完整的图片 show_element(c_full_bg) # 保存完整的图片 save_screenshot(c_full_bg, 'full')# 设置元素不可见def hide_element(element): browser.execute_script(\"arguments[0].style=arguments[1]\", element, \"display: none;\")# 设置元素可见def show_element(element): browser.execute_script(\"arguments[0].style=arguments[1]\", element, \"display: block;\")# 验证码截图函数def save_screenshot(obj, name): try: # 首先对出现验证码后的整个页面进行截图保存 pic_url = browser.save_screenshot('.\\\\bilibili.png') print(\"%s:截图成功!\" % pic_url) # 计算传入的obj,也就是三张图片的位置信息 left = obj.location['x'] top = obj.location['y'] right = left + obj.size['width'] bottom = top + obj.size['height'] # 打印输出一下每一张图的位置信息 print('图:' + name) print('Left %s' % left) print('Top %s' % top) print('Right %s' % right) print('Bottom %s' % bottom) print('') # 在整个页面截图的基础上,根据位置信息,分别剪裁出三张验证码图片并保存 im = Image.open('.\\\\bilibili.png') im = im.crop((left, top, right, bottom)) file_name = 'bili_' + name + '.png' im.save(file_name) except BaseException as msg: print(\"%s:截图失败!\" % msg)# 滑动模块的主函数def slide(): distance = get_distance(Image.open('.\\\\bili_back.png'), Image.open('.\\\\bili_full.png')) print('计算偏移量为:%s Px' % distance) trace = get_trace(distance - 5) move_to_gap(trace) time.sleep(3)# 计算滑块移动距离函数def get_distance(bg_image, fullbg_image): # 滑块的初始位置 distance = 60 # 遍历两张图片的每个像素 for i in range(distance, fullbg_image.size[0]): for j in range(fullbg_image.size[1]): # 调用缺口位置寻找函数 if not is_pixel_equal(fullbg_image, bg_image, i, j): return i# 缺口位置寻找函数def is_pixel_equal(bg_image, fullbg_image, x, y): # 获取两张图片对应像素点的RGB数据 bg_pixel = bg_image.load()[x, y] fullbg_pixel = fullbg_image.load()[x, y] # 设定一个阈值 threshold = 60 # 比较两张图 RGB 的绝对值是否均小于定义的阈值 if (abs(bg_pixel[0] - fullbg_pixel[0] < threshold) and abs(bg_pixel[1] - fullbg_pixel[1] < threshold) and abs( bg_pixel[2] - fullbg_pixel[2] < threshold)): return True else: return False# 构造移动轨迹函数def get_trace(distance): trace = [] # 设置加速距离为总距离的4/5 faster_distance = distance * (4 / 5) # 设置初始位置、初始速度、时间间隔 start, v0, t = 0, 0, 0.1 while start < distance: if start < faster_distance: a = 10 else: a = -10 # 位移 move = v0 * t + 1 / 2 * a * t * t # 当前时刻的速度 v = v0 + a * t v0 = v start += move trace.append(round(move)) # trace 记录了每个时间间隔移动了多少位移 return trace# 模拟拖动函数def move_to_gap(trace): # 获取滑动按钮 slider = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'div.geetest_slider_button'))) # 点击并拖动滑块 ActionChains(browser).click_and_hold(slider).perform() # 遍历运动轨迹获取每小段位移距离 for x in trace: # 移动此位移 ActionChains(browser).move_by_offset(xoffset=x, yoffset=0).perform() time.sleep(0.5) # 释放鼠标 ActionChains(browser).release().perform()if __name__ == '__main__': init() login() find_element() slide() 【6x00】效果实现动图最终实现效果图:(关键信息已经过打码处理)","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫实战","slug":"Python3-学习笔记/爬虫实战","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫实战/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"哔哩哔哩","slug":"哔哩哔哩","permalink":"https://www.itrhx.com/tags/哔哩哔哩/"}]},{"title":"Python3 爬虫实战 — 虎扑论坛步行街","slug":"A55-pyspider-hupu","date":"2019-10-12T15:28:23.380Z","updated":"2019-10-21T04:08:46.598Z","comments":true,"path":"2019/10/12/A55-pyspider-hupu/","link":"","permalink":"https://www.itrhx.com/2019/10/12/A55-pyspider-hupu/","excerpt":"爬取时间:2019-10-12爬取难度:★★☆☆☆☆请求链接:https://bbs.hupu.com/bxj爬取目标:爬取虎扑论坛步行街的帖子,包含主题,作者,发布时间等,数据保存到 MongoDB 数据库涉及知识:请求库 requests、解析库 Beautiful Soup、数据库 MongoDB 的操作完整代码:https://github.com/TRHX/Python3-Spider-Practice/tree/master/hupu其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278","text":"爬取时间:2019-10-12爬取难度:★★☆☆☆☆请求链接:https://bbs.hupu.com/bxj爬取目标:爬取虎扑论坛步行街的帖子,包含主题,作者,发布时间等,数据保存到 MongoDB 数据库涉及知识:请求库 requests、解析库 Beautiful Soup、数据库 MongoDB 的操作完整代码:https://github.com/TRHX/Python3-Spider-Practice/tree/master/hupu其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278 【1x00】循环爬取网页模块观察虎扑论坛步行街分区,请求地址为:https://bbs.hupu.com/bxj 第一页:https://bbs.hupu.com/bxj 第二页:https://bbs.hupu.com/bxj-2 第三页:https://bbs.hupu.com/bxj-3 不难发现,每增加一页,只需要添加 -页数 参数即可,最后一页是第 50 页,因此可以利用 for 循环依次爬取,定义一个 get_pages() 函数,返回初始化 Beautiful Soup 的对象 page_soup,方便后面的解析函数调用 虽然一共有 50 页,但是当用户访问第 10 页以后的页面的时候,会要求登录虎扑,不然就没法查看,而且登录时会出现智能验证,所以程序只爬取前 10 页的数据 123456789101112def get_pages(page_url): headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36' } response = requests.get(url=page_url, headers=headers) page_soup = BeautifulSoup(response.text, 'lxml') return page_soupif __name__ == '__main__': for i in range(1, 11): url = 'https://bbs.hupu.com/bxj-' + str(i) soup = get_pages(url) 【2x00】解析模块使用 Beautiful Soup 对网页各个信息进行提取,最后将这些信息放进一个列表里,然后调用列表的 .append() 方法,再将每条帖子的列表依次加到另一个新列表里,最终返回的是类似于如下形式的列表: 1[['帖子1', '作者1'], ['帖子2', '作者2'], ['帖子3', '作者3']] 这样做的目的是:方便 MongoDB 依次储存每一条帖子的信息 123456789101112131415161718192021222324252627282930313233343536373839def parse_pages(page_soup): data_list = [] all_list = page_soup.find('ul', class_='for-list') post_list = all_list.find_all('li') # print(result_list) for post in post_list: # 帖子名称 post_title = post.find('a', class_='truetit').text # print(post_title) # 帖子链接 post_url = 'https://bbs.hupu.com' + post.find('a', class_='truetit')['href'] # print(post_url) # 作者 author = post.select('.author > a')[0].text # print(author) # 作者主页 author_url = post.select('.author > a')[0]['href'] # print(author_url) # 发布日期 post_date = post.select('.author > a')[1].text # print(post_date) reply_view = post.find('span', class_='ansour').text # 回复数 post_reply = reply_view.split('/')[0].strip() # print(post_reply) # 浏览量 post_view = reply_view.split('/')[1].strip() # print(post_view) # 最后回复时间 last_data = post.select('.endreply > a')[0].text # print(last_data) # 最后回复用户 last_user = post.select('.endreply > span')[0].text # print(last_user) data_list.append([post_title, post_url, author, author_url, post_date, post_reply, post_view, last_data, last_user]) # print(data_list) return data_list 【3x00】MongoDB 数据储存模块首先使用 MongoClient() 方法,向其传入地址参数 host 和 端口参数 port,指定数据库为 hupu,集合为 bxj 将解析函数返回的列表传入到储存函数,依次循环该列表,对每一条帖子的信息进行提取并储存 1234567891011121314151617def mongodb(data_list): client = MongoClient('localhost', 27017) db = client.hupu collection = db.bxj for data in data_list: bxj = { '帖子名称': data[0], '帖子链接': data[1], '作者': data[2], '作者主页': data[3], '发布日期': str(data[4]), '回复数': data[5], '浏览量': data[6], '最后回复时间': str(data[7]), '最后回复用户': data[8] } collection.insert_one(bxj) 【4x00】完整代码1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495# =============================================# --*-- coding: utf-8 --*--# @Time : 2019-10-12# @Author : TRHX# @Blog : www.itrhx.com# @CSDN : https://blog.csdn.net/qq_36759224# @FileName: hupu.py# @Software: PyCharm# =============================================import requestsimport timeimport randomfrom pymongo import MongoClientfrom bs4 import BeautifulSoupdef get_pages(page_url): headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36' } response = requests.get(url=page_url, headers=headers) page_soup = BeautifulSoup(response.text, 'lxml') return page_soupdef parse_pages(page_soup): data_list = [] all_list = page_soup.find('ul', class_='for-list') post_list = all_list.find_all('li') # print(result_list) for post in post_list: # 帖子名称 post_title = post.find('a', class_='truetit').text # print(post_title) # 帖子链接 post_url = 'https://bbs.hupu.com' + post.find('a', class_='truetit')['href'] # print(post_url) # 作者 author = post.select('.author > a')[0].text # print(author) # 作者主页 author_url = post.select('.author > a')[0]['href'] # print(author_url) # 发布日期 post_date = post.select('.author > a')[1].text # print(post_date) reply_view = post.find('span', class_='ansour').text # 回复数 post_reply = reply_view.split('/')[0].strip() # print(post_reply) # 浏览量 post_view = reply_view.split('/')[1].strip() # print(post_view) # 最后回复时间 last_data = post.select('.endreply > a')[0].text # print(last_data) # 最后回复用户 last_user = post.select('.endreply > span')[0].text # print(last_user) data_list.append([post_title, post_url, author, author_url, post_date, post_reply, post_view, last_data, last_user]) # print(data_list) return data_listdef mongodb(data_list): client = MongoClient('localhost', 27017) db = client.hupu collection = db.bxj for data in data_list: bxj = { '帖子名称': data[0], '帖子链接': data[1], '作者': data[2], '作者主页': data[3], '发布日期': str(data[4]), '回复数': data[5], '浏览量': data[6], '最后回复时间': str(data[7]), '最后回复用户': data[8] } collection.insert_one(bxj)if __name__ == '__main__': for i in range(1, 11): url = 'https://bbs.hupu.com/bxj-' + str(i) soup = get_pages(url) result_list = parse_pages(soup) mongodb(result_list) print('第', i, '页数据爬取完毕!') time.sleep(random.randint(3, 10)) print('前10页所有数据爬取完毕!') 【5x00】数据截图一共爬取到 1180 条数据: 【6x00】程序不足的地方程序只能爬取前 10 页的数据,因为虎扑论坛要求从第 11 页开始,必须登录账号才能查看,并且登录时会有智能验证,可以使用自动化测试工具 Selenium 模拟登录账号后再进行爬取。","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫实战","slug":"Python3-学习笔记/爬虫实战","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫实战/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"虎扑论坛","slug":"虎扑论坛","permalink":"https://www.itrhx.com/tags/虎扑论坛/"}]},{"title":"Python3 爬虫实战 — 安居客武汉二手房","slug":"A54-pyspider-anjuke","date":"2019-10-09T15:02:42.994Z","updated":"2019-10-21T04:05:48.158Z","comments":true,"path":"2019/10/09/A54-pyspider-anjuke/","link":"","permalink":"https://www.itrhx.com/2019/10/09/A54-pyspider-anjuke/","excerpt":"爬取时间:2019-10-09爬取难度:★★☆☆☆☆请求链接:https://wuhan.anjuke.com/sale/爬取目标:爬取武汉二手房每一条售房信息,包含地理位置、价格、面积等,保存为 CSV 文件涉及知识:请求库 requests、解析库 Beautiful Soup、CSV 文件储存、列表操作、分页判断完整代码:https://github.com/TRHX/Python3-Spider-Practice/tree/master/anjuke其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278","text":"爬取时间:2019-10-09爬取难度:★★☆☆☆☆请求链接:https://wuhan.anjuke.com/sale/爬取目标:爬取武汉二手房每一条售房信息,包含地理位置、价格、面积等,保存为 CSV 文件涉及知识:请求库 requests、解析库 Beautiful Soup、CSV 文件储存、列表操作、分页判断完整代码:https://github.com/TRHX/Python3-Spider-Practice/tree/master/anjuke其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278 【1x00】页面整体分析分析 安居客武汉二手房页面,这次爬取实战准备使用 BeautifulSoup 解析库,熟练 BeautifulSoup 解析库的用法,注意到该页面与其他页面不同的是,不能一次性看到到底有多少页,以前知道一共有多少页,直接一个循环爬取就行了,虽然可以通过改变 url 来尝试找到最后一页,但是这样就显得不程序员了😂,因此可以通过 BeautifulSoup 解析 下一页按钮,提取到下一页的 url,直到没有 下一页按钮 这个元素为止,从而实现所有页面的爬取,剩下的信息提取和储存就比较简单了 【2x00】解析模块分析页面,可以发现每条二手房信息都是包含在 <li> 标签内的,因此可以使用 BeautifulSoup 解析页面得到所有的 <li> 标签,然后再循环访问每个 <li> 标签,依次解析得到每条二手房的各种信息 1234567891011121314151617181920212223242526272829303132333435363738394041def parse_pages(url, num): response = requests.get(url=url, headers=headers) soup = BeautifulSoup(response.text, 'lxml') result_list = soup.find_all('li', class_='list-item') # print(len(result_list)) for result in result_list: # 标题 title = result.find('a', class_='houseListTitle').text.strip() # print(title) # 户型 layout = result.select('.details-item > span')[0].text # print(layout) # 面积 cover = result.select('.details-item > span')[1].text # print(cover) # 楼层 floor = result.select('.details-item > span')[2].text # print(floor) # 建造年份 year = result.select('.details-item > span')[3].text # print(year) # 单价 unit_price = result.find('span', class_='unit-price').text.strip() # print(unit_price) # 总价 total_price = result.find('span', class_='price-det').text.strip() # print(total_price) # 关键字 keyword = result.find('div', class_='tags-bottom').text.strip() # print(keyword) # 地址 address = result.find('span', class_='comm-address').text.replace(' ', '').replace('\\n', '') # print(address) # 详情页url details_url = result.find('a', class_='houseListTitle')['href'] # print(details_url)if __name__ == '__main__': start_num = 0 start_url = 'https://wuhan.anjuke.com/sale/' parse_pages(start_url, start_num) 【3x00】循环爬取模块前面已经分析过,该网页是无法一下就能看到一共有多少页的,尝试找到最后一页,发现一共有50页,那么此时就可以搞个循环,一直到第50页就行了,但是如果有一天页面数增加了呢,那么代码的可维护性就不好了,我们可以观察 下一页按钮 ,当存在下一页的时候,是 <a> 标签,并且带有下一页的 URL,不存在下一页的时候是 <i> 标签,因此可以写个 if 语句,判断是否存在此 <a> 标签,若存在,表示有下一页,然后提取其 href 属性并传给解析模块,实现后面所有页面的信息提取,此外,由于安居客有反爬系统,我们还可以利用 Python中的 random.randint() 方法,在两个数值之间随机取一个数,传入 time.sleep() 方法,实现随机暂停爬取 12345678910# 判断是否还有下一页next_url = soup.find_all('a', class_='aNxt')if len(next_url) != 0: num += 1 print('第' + str(num) + '页数据爬取完毕!') # 3-60秒之间随机暂停 time.sleep(random.randint(3, 60)) parse_pages(next_url[0].attrs['href'], num)else: print('所有数据爬取完毕!') 【4x00】数据储存模块数据储存比较简单,将每个二手房信息组成一个列表,依次写入到 anjuke.csv 文件中即可 1234results = [title, layout, cover, floor, year, unit_price, total_price, keyword, address, details_url]with open('anjuke.csv', 'a', newline='', encoding='utf-8-sig') as f: w = csv.writer(f) w.writerow(results) 【5x00】完整代码123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081# =============================================# --*-- coding: utf-8 --*--# @Time : 2019-10-09# @Author : TRHX# @Blog : www.itrhx.com# @CSDN : https://blog.csdn.net/qq_36759224# @FileName: anjuke.py# @Software: PyCharm# =============================================import requestsimport timeimport csvimport randomfrom bs4 import BeautifulSoupheaders = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'}def parse_pages(url, num): response = requests.get(url=url, headers=headers) soup = BeautifulSoup(response.text, 'lxml') result_list = soup.find_all('li', class_='list-item') # print(len(result_list)) for result in result_list: # 标题 title = result.find('a', class_='houseListTitle').text.strip() # print(title) # 户型 layout = result.select('.details-item > span')[0].text # print(layout) # 面积 cover = result.select('.details-item > span')[1].text # print(cover) # 楼层 floor = result.select('.details-item > span')[2].text # print(floor) # 建造年份 year = result.select('.details-item > span')[3].text # print(year) # 单价 unit_price = result.find('span', class_='unit-price').text.strip() # print(unit_price) # 总价 total_price = result.find('span', class_='price-det').text.strip() # print(total_price) # 关键字 keyword = result.find('div', class_='tags-bottom').text.strip() # print(keyword) # 地址 address = result.find('span', class_='comm-address').text.replace(' ', '').replace('\\n', '') # print(address) # 详情页url details_url = result.find('a', class_='houseListTitle')['href'] # print(details_url) results = [title, layout, cover, floor, year, unit_price, total_price, keyword, address, details_url] with open('anjuke.csv', 'a', newline='', encoding='utf-8-sig') as f: w = csv.writer(f) w.writerow(results) # 判断是否还有下一页 next_url = soup.find_all('a', class_='aNxt') if len(next_url) != 0: num += 1 print('第' + str(num) + '页数据爬取完毕!') # 3-60秒之间随机暂停 time.sleep(random.randint(3, 60)) parse_pages(next_url[0].attrs['href'], num) else: print('所有数据爬取完毕!')if __name__ == '__main__': with open('anjuke.csv', 'a', newline='', encoding='utf-8-sig') as fp: writer = csv.writer(fp) writer.writerow(['标题', '户型', '面积', '楼层', '建造年份', '单价', '总价', '关键字', '地址', '详情页地址']) start_num = 0 start_url = 'https://wuhan.anjuke.com/sale/' parse_pages(start_url, start_num) 【6x00】数据截图 【7x00】程序不足的地方 虽然使用了随机暂停爬取的方法,但是在爬取了大约 20 页的数据后依然会出现验证页面,导致程序终止 原来设想的是可以由用户手动输入城市的拼音来查询不同城市的信息,方法是把用户输入的城市拼音和其他参数一起构造成一个 URL,然后对该 URL 发送请求,判断请求返回的代码,如果是 200 就代表可以访问,也就是用户输入的城市是正确的,然而发现即便是输入错误,该 URL 依然可以访问,只不过会跳转到一个正确的页面,没有搞清楚是什么原理,也就无法实现由用户输入城市来查询这个功能","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫实战","slug":"Python3-学习笔记/爬虫实战","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫实战/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"安居客","slug":"安居客","permalink":"https://www.itrhx.com/tags/安居客/"}]},{"title":"使用 Hexo-Git-Backup 插件备份你的 Hexo 博客","slug":"A53-hexo-backup","date":"2019-09-29T10:02:15.603Z","updated":"2019-12-29T07:19:49.434Z","comments":true,"path":"2019/09/29/A53-hexo-backup/","link":"","permalink":"https://www.itrhx.com/2019/09/29/A53-hexo-backup/","excerpt":"","text":"欢迎关注我的 CSDN 专栏:《个人博客搭建:Hexo+Github Pages》,从搭建到美化一条龙,帮你解决 Hexo 常见问题! 由于 Hexo 博客是静态托管的,所有的原始数据都保存在本地,如果哪一天电脑坏了,或者是误删了本地数据,那就是叫天天不应叫地地不灵了,此时定时备份就显得比较重要了,常见的备份方法有:打包数据保存到U盘、云盘或者其他地方,但是早就有大神开发了备份插件:hexo-git-backup ,只需要一个命令就可以将所有数据包括主题文件备份到 github 了 首先进入你博客目录,输入命令 hexo version 查看 Hexo 版本,如图所示,我的版本是 3.7.1: 安装备份插件,如果你的 Hexo 版本是 2.x.x,则使用以下命令安装: 1$ npm install hexo-git-backup@0.0.91 --save 如果你的 Hexo 版本是 3.x.x,则使用以下命令安装: 1$ npm install hexo-git-backup --save 到 Hexo 博客根目录的 _config.yml 配置文件里添加以下配置: 1234567backup: type: git theme: material-x-1.2.1 message: Back up my www.itrhx.com blog repository: github: git@github.com:TRHX/TRHX.github.io.git,backup coding: git@git.dev.tencent.com:TRHX/TRHX.git,backup 参数解释: theme:你要备份的主题名称 message:自定义提交信息 repository:仓库名,注意仓库地址后面要添加一个分支名,比如我就创建了一个 backup 分支 最后使用以下命令备份你的博客: 1$ hexo backup 或者使用以下简写命令也可以: 1$ hexo b 备份成功后可以在你的仓库分支下看到备份的原始文件:","categories":[{"name":"Hexo","slug":"Hexo","permalink":"https://www.itrhx.com/categories/Hexo/"}],"tags":[{"name":"Hexo","slug":"Hexo","permalink":"https://www.itrhx.com/tags/Hexo/"},{"name":"备份","slug":"备份","permalink":"https://www.itrhx.com/tags/备份/"}]},{"title":"Python3 爬虫实战 — 豆瓣电影TOP250","slug":"A52-pyspider-doubantop250","date":"2019-09-28T08:35:19.823Z","updated":"2019-10-21T04:01:29.248Z","comments":true,"path":"2019/09/28/A52-pyspider-doubantop250/","link":"","permalink":"https://www.itrhx.com/2019/09/28/A52-pyspider-doubantop250/","excerpt":"爬取时间:2019-09-27爬取难度:★★☆☆☆☆请求链接:https://movie.douban.com/top250 以及每部电影详情页爬取目标:爬取榜单上每一部电影详情页的数据,保存为 CSV 文件;下载所有电影海报到本地涉及知识:请求库 requests、解析库 lxml、Xpath 语法、正则表达式、CSV 和二进制数据储存、列表操作完整代码:https://github.com/TRHX/Python3-Spider-Practice/tree/master/douban-top250其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278","text":"爬取时间:2019-09-27爬取难度:★★☆☆☆☆请求链接:https://movie.douban.com/top250 以及每部电影详情页爬取目标:爬取榜单上每一部电影详情页的数据,保存为 CSV 文件;下载所有电影海报到本地涉及知识:请求库 requests、解析库 lxml、Xpath 语法、正则表达式、CSV 和二进制数据储存、列表操作完整代码:https://github.com/TRHX/Python3-Spider-Practice/tree/master/douban-top250其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278 【1x00】循环爬取网页模块观察豆瓣电影 Top 250,请求地址为:https://movie.douban.com/top250 每页展示25条电影信息,照例翻页观察 url 的变化: 第一页:https://movie.douban.com/top250 第二页:https://movie.douban.com/top250?start=25&filter= 第三页:https://movie.douban.com/top250?start=50&filter= 一共有10页,每次改变的是 start 的值,利用一个 for 循环,从 0 到 250 每隔 25 取一个值拼接到 url,实现循环爬取每一页,由于我们的目标是进入每一部电影的详情页,然后爬取详情页的内容,所以我们可以使用 Xpath 提取每一页每部电影详情页的 URL,将其赋值给 m_urls,并返回 m_urls,m_urls 是一个列表,列表元素就是电影详情页的 URL 12345678910def index_pages(number): url = 'https://movie.douban.com/top250?start=%s&filter=' % number index_response = requests.get(url=url, headers=headers) tree = etree.HTML(index_response.text) m_urls = tree.xpath(\"//li/div/div/a/@href\") return m_urlsif __name__ == '__main__': for i in range(0, 250, 25): movie_urls = index_pages(i) 【2x00】解析模块定义一个解析函数 parse_pages(),利用 for 循环,依次提取 index_pages() 函数返回的列表中的元素,也就是每部电影详情页的 URL,将其传给解析函数进行解析 1234567891011def index_pages(number): expressionsdef parse_pages(url): expressionsif __name__ == '__main__': for i in range(0, 250, 25): movie_urls = index_pages(i) for movie_url in movie_urls: results = parse_pages(movie_url) 详细看一下解析函数 parse_pages(),首先要对接收到的详情页 URL 发送请求,获取响应内容,然后再使用 Xpath 提取相关信息 123def parse_pages(url): movie_pages = requests.get(url=url, headers=headers) parse_movie = etree.HTML(movie_pages.text) 【2x01】Xpath 解析排名、电影名、评分信息其中排名、电影名和评分信息是最容易匹配到的,直接使用 Xpath 语法就可以轻松解决: 12345678# 排名ranking = parse_movie.xpath(\"//span[@class='top250-no']/text()\")# 电影名name = parse_movie.xpath(\"//h1/span[1]/text()\")# 评分score = parse_movie.xpath(\"//div[@class='rating_self clearfix']/strong/text()\") 【2x02】Xpath 解析参评人数接下来准备爬取有多少人参与了评价,分析一下页面: 如果只爬取这个 <span> 标签下的数字的话,没有任何提示信息,别人看了不知道是啥东西,所以把 人评价 这三个字也爬下来的话就比较好了,但是可以看到数字和文字不在同一个元素标签下,而且文字部分还有空格,要爬取的话就要把 class="rating_people" 的 a 标签下所有的 text 提取出来,然后再去掉空格: 123456789# 参评人数# 匹配a节点value = parse_movie.xpath(\"//a[@class='rating_people']\")# 提取a节点下所有文本string = [value[0].xpath('string(.)')]# 去除多余空格number = [a.strip() for a in string]# 此时 number = ['1617307人评价'] 这样做太麻烦了,我们可以直接提取数字,得到一个列表,然后使用另一个带有提示信息的列表,将两个列表的元素合并,组成一个新列表,这个新列表的元素就是提示信息+人数123456# 参评人数value = parse_movie.xpath(\"//span[@property='v:votes']/text()\")# 合并元素number = [\" \".join(['参评人数:'] + value)]# 此时 number = ['参评人数:1617307'] 【2x03】正则表达式解析制片国家、语言接下来尝试爬取制片国家/地区、语言等信息: 分析页面可以观察到,制片国家/地区和语言结构比较特殊,没有特别的 class 或者 id 属性,所包含的层次关系也太复杂,所以这里为了简便,直接采用正则表达式来匹配信息,就没有那么复杂了: 1234567# 制片国家/地区value = re.findall('<span class=\"pl\">制片国家/地区:</span>(.*?)<br/>', movie_pages.text)country = [\" \".join(['制片国家:'] + value)]# 语言value = re.findall('<span class=\"pl\">语言:</span>(.*?)<br/>', movie_pages.text)language = [\" \".join(['语言:'] + value)] 【3x00】返回解析数据其他剩下的信息皆可利用以上方法进行提取,所有信息提取完毕,最后使用 zip() 函数,将所有提取的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表 1return zip(ranking, name, score, number, types, country, language, date, time, other_name, director, screenwriter, performer, m_url, imdb_url) 【4x00】数据储存模块定义一个数据保存函数 save_results() 1234def save_results(data): with open('douban.csv', 'a', encoding=\"utf-8-sig\") as fp: writer = csv.writer(fp) writer.writerow(data) 注意:编码方式要设置为 utf-8-sig,如果设置为 utf-8,则文件会乱码,不设置编码,则可能会报一下类似错误: 1UnicodeEncodeError: 'gbk' codec can't encode character '\\ub3c4' in position 9: illegal multibyte sequence 可以看到错误出现在 \\ub3c4 上,将该 Unicode 编码转换为中文为 도,发现正是排名第 19 的电影:熔炉 도가니,因为标题有韩文,所以在储存为 CSV 文件时会报编码错误,而将编码设置为 utf-8-sig 就不会报错,具体原因参见:《Python 中文日文汉字乱码处理utf-8-sig》 接下来是保存电影的海报到本地: 1234567891011# 保存电影海报poster = parse_movie.xpath(\"//div[@id='mainpic']/a/img/@src\")response = requests.get(poster[0])name2 = re.sub(r'[A-Za-z\\:\\s]', '', name[0])poster_name = str(ranking[0]) + ' - ' + name2 + '.jpg'dir_name = 'douban_poster'if not os.path.exists(dir_name): os.mkdir(dir_name)poster_path = dir_name + '/' + poster_namewith open(poster_path, \"wb\")as f: f.write(response.content) 解析电影详情页,使用 Xpath 提取海报的 URL,向该 URL 发送请求 图片以 排名+电影名.jpg 的方式命名,但是由于提取的电影名部分含有特殊字符,比如排名第 10 的电影:忠犬八公的故事 Hachi: A Dog’s Tale,其中有个冒号,而 Windows 文件命名是不能包含这些字符的,所以我们直接去除电影名包含的英文字符、空白字符、特殊字符,只留下中文,代码实现: name2 = re.sub(r'[A-Za-z\\:\\s]', '', name[0]) 定义一个文件夹名称 douban_poster,利用 os 模块判断当前是否存在该文件夹,若不存在就创建一个 最后以二进制形式保存海报到当前目录的 douban_poster 文件夹下 【5x00】完整代码123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124# =============================================# --*-- coding: utf-8 --*--# @Time : 2019-09-27# @Author : TRHX# @Blog : www.itrhx.com# @CSDN : https://blog.csdn.net/qq_36759224# @FileName: douban.py# @Software: PyCharm# =============================================import requestsfrom lxml import etreeimport csvimport reimport timeimport osheaders = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'}def index_pages(number): url = 'https://movie.douban.com/top250?start=%s&filter=' % number index_response = requests.get(url=url, headers=headers) tree = etree.HTML(index_response.text) m_urls = tree.xpath(\"//li/div/div/a/@href\") return m_urlsdef parse_pages(url): movie_pages = requests.get(url=url, headers=headers) parse_movie = etree.HTML(movie_pages.text) # 排名 ranking = parse_movie.xpath(\"//span[@class='top250-no']/text()\") # 电影名 name = parse_movie.xpath(\"//h1/span[1]/text()\") # 评分 score = parse_movie.xpath(\"//div[@class='rating_self clearfix']/strong/text()\") # 参评人数 value = parse_movie.xpath(\"//span[@property='v:votes']/text()\") number = [\" \".join(['参评人数:'] + value)] # value = parse_movie.xpath(\"//a[@class='rating_people']\") # string = [value[0].xpath('string(.)')] # number = [a.strip() for a in string] # print(number) # 类型 value = parse_movie.xpath(\"//span[@property='v:genre']/text()\") types = [\" \".join(['类型:'] + value)] # 制片国家/地区 value = re.findall('<span class=\"pl\">制片国家/地区:</span>(.*?)<br/>', movie_pages.text) country = [\" \".join(['制片国家:'] + value)] # 语言 value = re.findall('<span class=\"pl\">语言:</span>(.*?)<br/>', movie_pages.text) language = [\" \".join(['语言:'] + value)] # 上映时期 value = parse_movie.xpath(\"//span[@property='v:initialReleaseDate']/text()\") date = [\" \".join(['上映日期:'] + value)] # 片长 value = parse_movie.xpath(\"//span[@property='v:runtime']/text()\") time = [\" \".join(['片长:'] + value)] # 又名 value = re.findall('<span class=\"pl\">又名:</span>(.*?)<br/>', movie_pages.text) other_name = [\" \".join(['又名:'] + value)] # 导演 value = parse_movie.xpath(\"//div[@id='info']/span[1]/span[@class='attrs']/a/text()\") director = [\" \".join(['导演:'] + value)] # 编剧 value = parse_movie.xpath(\"//div[@id='info']/span[2]/span[@class='attrs']/a/text()\") screenwriter = [\" \".join(['编剧:'] + value)] # 主演 value = parse_movie.xpath(\"//div[@id='info']/span[3]\") performer = [value[0].xpath('string(.)')] # URL m_url = ['豆瓣链接:' + movie_url] # IMDb链接 value = parse_movie.xpath(\"//div[@id='info']/a/@href\") imdb_url = [\" \".join(['IMDb链接:'] + value)] # 保存电影海报 poster = parse_movie.xpath(\"//div[@id='mainpic']/a/img/@src\") response = requests.get(poster[0]) name2 = re.sub(r'[A-Za-z\\:\\s]', '', name[0]) poster_name = str(ranking[0]) + ' - ' + name2 + '.jpg' dir_name = 'douban_poster' if not os.path.exists(dir_name): os.mkdir(dir_name) poster_path = dir_name + '/' + poster_name with open(poster_path, \"wb\")as f: f.write(response.content) return zip(ranking, name, score, number, types, country, language, date, time, other_name, director, screenwriter, performer, m_url, imdb_url)def save_results(data): with open('douban.csv', 'a', encoding=\"utf-8-sig\") as fp: writer = csv.writer(fp) writer.writerow(data)if __name__ == '__main__': num = 0 for i in range(0, 250, 25): movie_urls = index_pages(i) for movie_url in movie_urls: results = parse_pages(movie_url) for result in results: num += 1 save_results(result) print('第' + str(num) + '条电影信息保存完毕!') time.sleep(3) 【6x00】数据截图 【7x00】程序不足的地方程序不足的地方:豆瓣电影有反爬机制,当程序爬取到大约 150 条数据的时候,IP 就会被封掉,第二天 IP 才会解封,可以考虑综合使用多个代理、多个 User-Agent、随机时间暂停等方法进行爬取","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫实战","slug":"Python3-学习笔记/爬虫实战","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫实战/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"豆瓣电影","slug":"豆瓣电影","permalink":"https://www.itrhx.com/tags/豆瓣电影/"}]},{"title":"Python3 爬虫实战 — 猫眼电影TOP100","slug":"A51-pyspider-maoyantop100","date":"2019-09-24T11:31:56.965Z","updated":"2019-10-21T04:00:20.669Z","comments":true,"path":"2019/09/24/A51-pyspider-maoyantop100/","link":"","permalink":"https://www.itrhx.com/2019/09/24/A51-pyspider-maoyantop100/","excerpt":"爬取时间:2019-09-23爬取难度:★☆☆☆☆☆请求链接:https://maoyan.com/board/4爬取目标:猫眼 TOP100 的电影名称、排名、主演、上映时间、评分、封面图地址,数据保存为 CSV 文件涉及知识:请求库 requests、解析库 lxml、Xpath 语法、CSV 文件储存完整代码:https://github.com/TRHX/Python3-Spider-Practice/tree/master/maoyan-top100其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278","text":"爬取时间:2019-09-23爬取难度:★☆☆☆☆☆请求链接:https://maoyan.com/board/4爬取目标:猫眼 TOP100 的电影名称、排名、主演、上映时间、评分、封面图地址,数据保存为 CSV 文件涉及知识:请求库 requests、解析库 lxml、Xpath 语法、CSV 文件储存完整代码:https://github.com/TRHX/Python3-Spider-Practice/tree/master/maoyan-top100其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278 【1x00】循环爬取网页模块观察猫眼电影TOP100榜,请求地址为:https://maoyan.com/board/4 每页展示10条电影信息,翻页观察 url 变化: 第一页:https://maoyan.com/board/4 第二页:https://maoyan.com/board/4?offset=10 第三页:https://maoyan.com/board/4?offset=20 一共有10页,利用一个 for 循环,从 0 到 100 每隔 10 取一个值拼接到 url,实现循环爬取每一页 12345678def index_page(number): url = 'https://maoyan.com/board/4?offset=%s' % number response = requests.get(url=url, headers=headers) return response.textif __name__ == '__main__': for i in range(0, 100, 10): index = index_page(i) 【2x00】解析模块定义一个页面解析函数 parse_page(),使用 lxml 解析库的 Xpath 方法依次提取电影排名(ranking)、电影名称(movie_name)、主演(performer)、上映时间(releasetime)、评分(score)、电影封面图 url(movie_img) 通过对主演部分的提取发现有多余的空格符和换行符,循环 performer 列表,使用 strip() 方法去除字符串头尾空格和换行符 电影评分分为整数部分和小数部分,依次提取两部分,循环遍历组成一个完整的评分 最后使用 zip() 函数,将所有提取的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表 123456789101112131415161718def parse_page(content): tree = etree.HTML(content) # 电影排名 ranking = tree.xpath(\"//dd/i/text()\") # 电影名称 movie_name = tree.xpath('//p[@class=\"name\"]/a/text()') # 主演 performer = tree.xpath(\"//p[@class='star']/text()\") performer = [p.strip() for p in performer] # 上映时间 releasetime = tree.xpath('//p[@class=\"releasetime\"]/text()') # 评分 score1 = tree.xpath('//p[@class=\"score\"]/i[@class=\"integer\"]/text()') score2 = tree.xpath('//p[@class=\"score\"]/i[@class=\"fraction\"]/text()') score = [score1[i] + score2[i] for i in range(min(len(score1), len(score2)))] # 电影封面图 movie_img = tree.xpath('//img[@class=\"board-img\"]/@data-src') return zip(ranking, movie_name, performer, releasetime, score, movie_img) 【3x00】数据储存模块定义一个 save_results() 函数,将所有数据保存到 maoyan.csv 文件 1234def save_results(result): with open('maoyan.csv', 'a') as fp: writer = csv.writer(fp) writer.writerow(result) 【4x00】完整代码1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859# =============================================# --*-- coding: utf-8 --*--# @Time : 2019-09-23# @Author : TRHX# @Blog : www.itrhx.com# @CSDN : https://blog.csdn.net/qq_36759224# @FileName: maoyan.py# @Software: PyCharm# =============================================import requestsfrom lxml import etreeimport csvheaders = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'}def index_page(number): url = 'https://maoyan.com/board/4?offset=%s' % number response = requests.get(url=url, headers=headers) return response.textdef parse_page(content): tree = etree.HTML(content) # 电影排名 ranking = tree.xpath(\"//dd/i/text()\") # 电影名称 movie_name = tree.xpath('//p[@class=\"name\"]/a/text()') # 主演 performer = tree.xpath(\"//p[@class='star']/text()\") performer = [p.strip() for p in performer] # 上映时间 releasetime = tree.xpath('//p[@class=\"releasetime\"]/text()') # 评分 score1 = tree.xpath('//p[@class=\"score\"]/i[@class=\"integer\"]/text()') score2 = tree.xpath('//p[@class=\"score\"]/i[@class=\"fraction\"]/text()') score = [score1[i] + score2[i] for i in range(min(len(score1), len(score2)))] # 电影封面图 movie_img = tree.xpath('//img[@class=\"board-img\"]/@data-src') return zip(ranking, movie_name, performer, releasetime, score, movie_img)def save_results(result): with open('maoyan.csv', 'a') as fp: writer = csv.writer(fp) writer.writerow(result)if __name__ == '__main__': print('开始爬取数据...') for i in range(0, 100, 10): index = index_page(i) results = parse_page(index) for i in results: save_results(i) print('数据爬取完毕!') 【4x00】数据截图","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫实战","slug":"Python3-学习笔记/爬虫实战","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫实战/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"猫眼电影","slug":"猫眼电影","permalink":"https://www.itrhx.com/tags/猫眼电影/"}]},{"title":"Python3 爬虫学习笔记 C18","slug":"A50-Python3-spider-C18","date":"2019-09-21T03:59:30.358Z","updated":"2019-09-24T12:41:19.337Z","comments":true,"path":"2019/09/21/A50-Python3-spider-C18/","link":"","permalink":"https://www.itrhx.com/2019/09/21/A50-Python3-spider-C18/","excerpt":"Python3 爬虫学习笔记第十八章 —— 【爬虫框架 pyspider — 深入理解】","text":"Python3 爬虫学习笔记第十八章 —— 【爬虫框架 pyspider — 深入理解】 【18.1】启动参数常用启动命令:pyspider all,完整命令结构为:pyspider [OPTIONS] COMMAND [ARGS],OPTIONS 为可选参数,包含以下参数: -c, –config FILENAME:指定配置文件名称 –logging-config TEXT:日志配置文件名称,默认: pyspider/pyspider/logging.conf –debug:开启调试模式 –queue-maxsize INTEGER:队列的最大长度 –taskdb TEXT:taskdb 的数据库连接字符串,默认: sqlite –projectdb TEXT:projectdb 的数据库连接字符串,默认: sqlite –resultdb TEXT:resultdb 的数据库连接字符串,默认: sqlite –message-queue TEXT:消息队列连接字符串,默认: multiprocessing.Queue –phantomjs-proxy TEXT:PhantomJS 使用的代理,ip:port 的形式 –data-path TEXT:数据库存放的路径 –add-sys-path / –not-add-sys-path:将当前工作目录添加到python lib搜索路径 –version:显示 pyspider 的版本信息 –help:显示帮助信息 配置文件为一个 JSON 文件,一般为 config.json 文件,常用配置如下: 123456789101112{ \"taskdb\": \"mysql+taskdb://username:password@host:port/taskdb\", \"projectdb\": \"mysql+projectdb://username:password@host:port/projectdb\", \"resultdb\": \"mysql+resultdb://username:password@host:port/resultdb\", \"message_queue\": \"amqp://username:password@host:port/%2F\", \"webui\": { \"port\": 5000, \"username\": \"some_name\", \"password\": \"some_passwd\", \"need-auth\": true }} 可以设置对应的用户名,密码,端口等信息,使用命令 pyspider -c config.json all 即可运行 【18.2】运行单个组件pyspider 的架构主要分为 Scheduler(调度器)、Fetcher(抓取器)、Processer(处理器)三个部分,都可以单独运行,基本命令: pyspider [component_name] [options] 【18.2.1】运行 Scheduler1pyspider scheduler [OPTIONS] 123456789101112Options: --xmlrpc /--no-xmlrpc --xmlrpc-host TEXT --xmlrpc-port INTEGER --inqueue-limit INTEGER 任务队列的最大长度,如果满了则新的任务会被忽略 --delete-time INTEGER 设置为 delete 标记之前的删除时间 --active-tasks INTEGER 当前活跃任务数量配置 --loop-limit INTEGER 单轮最多调度的任务数量 --fail-pause-num INTEGER 上次失败时自动暂停项目暂停次数,任务失败,将0设置为禁用 --scheduler-cls TEXT Scheduler 使用的类 --threads TEXT ThreadBaseScheduler 的线程号,默认值:4 --help 显示帮助信息 【18.2.2】运行 Fetcher1pyspider fetcher [OPTIONS] 123456789101112Options: --xmlrpc /--no-xmlrpc --xmlrpc-host TEXT --xmlrpc-port INTEGER --poolsize INTEGER 同时请求的个数 --proxy TEXT 使用的代理 --user-agent TEXT 使用的 User-Agent --timeout TEXT 超时时间 --phantomjs-endpoint TEXT phantomjs 的端点,通过 pyspider 启动 phantomjs --splash-endpoint TEXT 执行 splash 的端点:http://splash.readthedocs.io/en/stable/api.html execut --fetcher-cls TEXT Fetcher 使用的类 --help 显示帮助信息 【18.2.3】运行 Processer1pyspider processor [OPTIONS] 1234Options: --processor-cls TEXT Processor 使用的类 --process-time-limit INTEGER 脚本处理时间限制 --help 显示帮助信息 【18.2.4】运行 WebUI1pyspider webui [OPTIONS] 1234567891011121314Options: --host TEXT 运行地址 --port INTEGER 运行端口 --cdn TEXT JS 和 CSS 的 CDN 服务器 --scheduler-rpc TEXT Scheduler 的 xmlrpc 路径 --fetcher-rpc TEXT Fetcher 的 xmlrpc 路径 --max-rate FLOAT 每个项目最大的 rate 值 --max-burst FLOAT 每个项目最大的 burst 值 --username TEXT Auth 验证的用户名 --password TEXT Auth 验证的密码 --need-auth 是否需要验证 --webui-instance TEXT 运行时使用的 Flask 应用 --process-time-limit INTEGER 调试中的脚本处理时间限制 --help 显示帮助信息 【18.3】crawl() 方法各参数参数文档:http://docs.pyspider.org/en/latest/apis/self.crawl/ url:爬取目标 URL,可以定义为单个 URL 字符串,也可以定义成 URL 列表 callback:回调函数,指定了该 URL 对应的响应内容用哪个方法来解析,示例: 12def on_start(self): self.crawl('http://www.itrhx.com/', callback=self.index_page) 代码解释:指定 callback 为 index_page,代表爬取 http://www.itrhx.com/ 得到的响应会用 index_page() 方法来解析,而 index_page() 方法的第一个参数就是响应对象,如下所示: 12def index_page(self, response): pass age:任务的有效时间,如果某个任务在有效时间内且已经被执行,则它不会重复执行,有如下两种设置方法: 12def on_start(self): self.crawl('http://www.itrhx.com/', callback=self.callback, age=10*24*60*60) 123@config(age=10 * 24 * 60 * 60)def callback(self): pass priority:爬取任务的优先级,其值默认是 0,priority 的数值越大,对应的请求会越优先被调度,如下所示,2.html 页面将会优先爬取: 123def index_page(self): self.crawl('http://www.itrhx.com/1.html', callback=self.index_page) self.crawl('http://www.itrhx.com/2.html', callback=self.detail_page, priority=1) exetime:设置定时任务,其值是时间戳,默认是 0,即代表立即执行,如下所示表示该任务会在 30 分钟之后执行: 123import timedef on_start(self): self.crawl('http://www.itrhx.com/', callback=self.callback, exetime=time.time()+30*60) retries:定义重试次数,其值默认是 3 itag:设置判定网页是否发生变化的节点值,在爬取时会判定次当前节点是否和上次爬取到的节点相同。如果节点相同,则证明页面没有更新,就不会重复爬取,如下所示: 123def index_page(self, response): for item in response.doc('.item').items(): self.crawl(item.find('a').attr.url, callback=self.detail_page, itag=item.find('.update-time').text()) 代码解释:设置 update-time 这个节点的值为 itag,在下次爬取时就会首先检测这个值有没有发生变化,如果没有变化,则不再重复爬取,否则执行爬取 auto_recrawl:开启时,爬取任务在过期后会重新执行,循环时间即定义的 age 时间长度,如下所示: 12def on_start(self): self.crawl('http://www.itrhx.com/', callback=self.callback, age=5*60*60, auto_recrawl=True) 代码解释:定义 age 有效期为 5 小时,设置了 auto_recrawl 为 True,这样任务就会每 5 小时执行一次 method:HTTP 请求方式,默认为 GET,如果想发起 POST 请求,可以将 method 设置为 POST params:定义 GET 请求参数,如下所示表示两个等价的爬取任务: 123def on_start(self): self.crawl('http://httpbin.org/get', callback=self.callback, params={'a': 123, 'b': 'c'}) self.crawl('http://httpbin.org/get?a=123&b=c', callback=self.callback) data:POST 表单数据,当请求方式为 POST 时,我们可以通过此参数传递表单数据,如下所示: 12def on_start(self): self.crawl('http://httpbin.org/post', callback=self.callback, method='POST', data={'a': 123, 'b': 'c'}) files:上传的文件,需要指定文件名,如下所示: 12def on_start(self): self.crawl('http://httpbin.org/post', callback=self.callback, method='POST', files={field: {filename: 'content'}}) user_agent:爬取使用的 User-Agent headers:爬取时使用的 Headers,即 Request Headers cookies:爬取时使用的 Cookies,为字典格式 connect_timeout:在初始化连接时的最长等待时间,默认为 20 秒 timeout:抓取网页时的最长等待时间,默认为 120 秒 allow_redirects:确定是否自动处理重定向,默认为 True validate_cert:确定是否验证证书,此选项对 HTTPS 请求有效,默认为 True proxy:爬取时使用的代理,支持用户名密码的配置,格式为 username:password@hostname:port,如下所示: 12def on_start(self): self.crawl('http://httpbin.org/get', callback=self.callback, proxy='127.0.0.1:9743') 也可以设置 craw_config 来实现全局配置,如下所示: 12class Handler(BaseHandler): crawl_config = {'proxy': '127.0.0.1:9743'} fetch_type:开启 PhantomJS 渲染,如果遇到 JavaScript 渲染的页面,指定此字段即可实现 PhantomJS 的对接,pyspider 将会使用 PhantomJS 进行网页的抓取,如下所示: 12def on_start(self): self.crawl('https://www.taobao.com', callback=self.index_page, fetch_type='js') js_script:页面加载完毕后执行的 JavaScript 脚本,如下所示,页面加载成功后将执行页面混动的 JavaScript 代码,页面会下拉到最底部: 1234567def on_start(self): self.crawl('http://www.example.org/', callback=self.callback, fetch_type='js', js_script=''' function() {window.scrollTo(0,document.body.scrollHeight); return 123; } ''') js_run_at:代表 JavaScript 脚本运行的位置,是在页面节点开头还是结尾,默认是结尾,即 document-end js_viewport_width/js_viewport_height:JavaScript 渲染页面时的窗口大小 load_images:在加载 JavaScript 页面时确定是否加载图片,默认为否 save:在不同的方法之间传递参数,如下所示: 123456def on_start(self): self.crawl('http://www.example.org/', callback=self.callback, save={'page': 1})def callback(self, response): return response.save['page'] cancel:取消任务,如果一个任务是 ACTIVE 状态的,则需要将 force_update 设置为 True force_update:即使任务处于 ACTIVE 状态,那也会强制更新状态 【18.4】任务区分pyspider 判断两个任务是否是重复的是使用的是该任务对应的 URL 的 MD5 值作为任务的唯一 ID,如果 ID 相同,那么两个任务就会判定为相同,其中一个就不会爬取了 某些情况下,请求的链接是同一个,但是 POST 的参数不同,这时可以重写 task_id() 方法,利用 URL 和 POST 的参数来生成 ID,改变这个 ID 的计算方式来实现不同任务的区分: 1234import jsonfrom pyspider.libs.utils import md5stringdef get_taskid(self, task): return md5string(task['url']+json.dumps(task['fetch'].get('data', ''))) 【18.5】全局配置pyspider 可以使用 crawl_config 来指定全局的配置,配置中的参数会和 crawl() 方法创建任务时的参数合并: 12345class Handler(BaseHandler): crawl_config = { 'headers': {'User-Agent': 'GoogleBot',} 'proxy': '127.0.0.1:9743' } 【18.6】定时爬取通过 every 属性来设置爬取的时间间隔,如下代码表示每天执行一次爬取: 1234@every(minutes=24 * 60)def on_start(self): for url in urllist: self.crawl(url, callback=self.index_page) 注意事项:如果设置了任务的有效时间(age 参数),因为在有效时间内爬取不会重复,所以要把有效时间设置得比重复时间更短,这样才可以实现定时爬取 错误举例:设定任务的过期时间为 5 天,而自动爬取的时间间隔为 1 天,当第二次尝试重新爬取的时候,pyspider 会监测到此任务尚未过期,便不会执行爬取: 1234567@every(minutes=24 * 60)def on_start(self): self.crawl('http://www.itrhx.com/', callback=self.index_page)@config(age=5 * 24 * 60 * 60)def index_page(self): pass","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫学习","slug":"Python3-学习笔记/爬虫学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫学习/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"pyspider","slug":"pyspider","permalink":"https://www.itrhx.com/tags/pyspider/"}]},{"title":"Python3 爬虫学习笔记 C17","slug":"A49-Python3-spider-C17","date":"2019-09-18T06:18:23.904Z","updated":"2019-09-24T12:41:15.652Z","comments":true,"path":"2019/09/18/A49-Python3-spider-C17/","link":"","permalink":"https://www.itrhx.com/2019/09/18/A49-Python3-spider-C17/","excerpt":"Python3 爬虫学习笔记第十七章 —— 【爬虫框架 pyspider — 基本使用】","text":"Python3 爬虫学习笔记第十七章 —— 【爬虫框架 pyspider — 基本使用】 【17.1】初识 pyspiderpyspider 是由国人 Binux 编写的一个 Python 爬虫框架 GitHub:https://github.com/binux/pyspider 官方文档(英文):http://docs.pyspider.org/ 非官方文档(中文):http://book.crifan.com/books/python_spider_pyspider/website/ 非官方文档(中文):https://www.cntofu.com/book/156/index.md pyspider 特性: python 脚本控制,可以使用任何 html 解析包(内置 pyquery) WEB 界面编写调试脚本,起停脚本,监控执行状态,查看活动历史,获取结果产出 支持 MySQL、MongoDB、Redis、SQLite、Elasticsearch、PostgreSQL 对接了 PhantomJS,支持抓取 JavaScript 的页面 组件可替换,支持单机和分布式部署,支持 Docker 部署 提供优先级控制、失败重试、定时抓取等功能 Windows 系统安装 pyspider: 使用命令 pip install pyspider 安装,若报 PyCurl 相关错误,可访问 https://www.lfd.uci.edu/~gohlke/pythonlibs/#pycurl 下载对应 wheel 文件并使用命令 pip install whl文件名 安装即可 如果要爬取 JavaScrip 渲染的页面,还要下载 PhantomJS,并将 PhantomJS 的路径配置到环境变量里,或者直接复制到 Python 安装目录的 Scripts 文件夹,需要用到数据库储存的话,同样要安装好相应的数据库 准备就绪后,使用 pyspider all 命令可启动 pyspider,浏览器打开:http://localhost:5000/ 可以看到 pyspider 的 WebUI 管理界面 【17.2】使用 pyspider 【17.2.1】主界面当成功创建了一个爬虫项目后,主界面如下所示: Recent Active Tasks:查看最近活动的任务,会跳转到一个页面有列表显示 Create:创建一个新的爬虫项目 group:定义项目的分组,以方便管理,若 group 设置为 delete,则该项目将会在24小时之后删除 project name:爬虫项目名称 status:项目状态,各状态如下: TODO:一个爬虫项目刚刚创建时的状态,此状态下可以编辑 Python 代码 STOP:中止项目的运行 CHECKING:当一个运行中的项目被编辑时项目状态会被自动设置成此状态并中止运行 DEBUG:会运行爬虫,顾名思义找 BUG,一般来说用于调试阶段 RUNNING:运行爬虫项目 PAUSED:项目暂停运行,默认没有这个状态,但是当你在运行过程中突然断网就会出现此状态 rate/burst:当前的爬取速率,rate 代表 1 秒发出多少个请求,burst 相当于流量控制中的令牌桶算法的令牌数,rate 和 burst 设置的越大,爬取速率越快,速率的设定需要考虑本机性能和爬取过快被封的问题 avg time:任务平均时间 process:5m、1h、1d 分别指的是最近 5 分、1 小时、1 天内的请求情况,all 代表所有的请求情况,请求由不同颜色表示,蓝色的代表等待被执行的请求,绿色的代表成功的请求,黄色的代表请求失败后等待重试的请求,红色的代表失败次数过多而被忽略的请求 actions:对爬虫项目的操作,各操作如下: Run:立即执行任务,需要 status 为 RUNNING 或者 DEBUG 状态;假如在配置的调度执行时间内已经执行过,再点 run 是无效的,需要删除 task.db 里的数据才行 Active Tasks:查看当前爬虫项目的活动任务 Results:查看项目运行结果 【17.2.2】项目界面创建一个爬虫项目,界面如下所示: 创建项目:点击 Create 即可新建一个爬虫项目 Project Name:爬虫项目名称 Start URL(s) :爬虫入口地址,选填,可在项目中更改 项目创建完成进入调试界面: 调试界面右边:编写代码的区域 调试界面左边:调试的区域,用于执行代码,显示输出信息等用途 run:单步调试爬虫程序,点击就可运行当前任务 < > 箭头:上一步、下一步,用于调试过程中切换到上一步骤或者下一步骤 save:保存当前代码,当代码变更后只有保存了再运行才能得到最新结果 enable css selector helper: CSS 选择器辅助程序 web:页面预览 html:可以查看页面源代码 follows:表示爬取请求,点击可查看所有的请求 在新建一个爬虫项目的时候,pyspider 已经自动生成了如下代码: 123456789101112131415161718192021222324252627#!/usr/bin/env python# -*- encoding: utf-8 -*-# Created on 2019-09-17 21:18:13# Project: 2from pyspider.libs.base_handler import *class Handler(BaseHandler): crawl_config = { } @every(minutes=24 * 60) def on_start(self): self.crawl('__START_URL__', callback=self.index_page) @config(age=10 * 24 * 60 * 60) def index_page(self, response): for each in response.doc('a[href^=\"http\"]').items(): self.crawl(each.attr.href, callback=self.detail_page) @config(priority=2) def detail_page(self, response): return { \"url\": response.url, \"title\": response.doc('title').text(), } class Handler():pyspider 爬虫的主类,可以在此处定义爬取、解析、存储的逻辑。整个爬虫的功能只需要一个 Handler 即可完成 crawl_config 属性:项目的所有爬取配置将会统一定义到这里,如定义 headers、设置代理等,配置之后全局生效 on_start() 方法:爬取入口,初始的爬取请求会在这里产生,该方法通过调用 crawl() 方法即可新建一个爬取请求,第一个参数是爬取的 URL,另一个参数 callback 指定了这个页面爬取成功后用哪个方法进行解析,默认指定为 index_page() 方法,即如果这个 URL 对应的页面爬取成功了,那 Response 将交给 index_page() 方法解析 index_page() 方法:接收 Response 参数,Response 对接了 pyquery。直接调用 doc() 方法传入相应的 CSS 选择器,就可以像 pyquery 一样解析此页面,代码中默认是 a[href^="http"],即解析页面的所有链接,然后将链接遍历,再次调用了 crawl() 方法生成了新的爬取请求,同时再指定了 callback 为 detail_page,表示这些页面爬取成功了就调用 detail_page() 方法解析。index_page() 实现了两个功能,一是将爬取的结果进行解析,二是生成新的爬取请求 detail_page() 方法:同样接收 Response 作为参数。detail_page() 抓取的就是详情页的信息,就不会生成新的请求,只对 Response 对象做解析,解析之后将结果以字典的形式返回。当然也可以进行后续处理,如将结果保存到数据库等操作 PS:pyspider 默认的 web 预览页面窗口较小,可以找到 pyspider 文件夹有个 debug.min.css 文件(如:E:\\Python\\Lib\\site-packages\\pyspider\\webui\\static\\debug.min.css),搜索 iframe,将原样式:iframe{border-width:0;width:100%} 改为 iframe{border-width:0;width:100%;height:400px !important} 即可,清除浏览器缓存后就会生效! 【17.3】使用 pyspider 爬取去哪儿网爬取地址:http://travel.qunar.com/travelbook/list.htm爬取目标:去哪儿网旅游攻略,发帖作者、标题、正文等 【17.3.1】爬取首页创建一个名为 qunar 的爬虫项目,Start URL 设置为 http://travel.qunar.com/travelbook/list.htm ,点击 run 出现一个爬取请求 左边调试区域出现以下代码: 12345678{ \"process\": { \"callback\": \"on_start\" }, \"project\": \"qunar\", \"taskid\": \"data:,on_start\", \"url\": \"data:,on_start\"} callback 为 on_start,表示此时执行了 on_start() 方法。在 on_start() 方法中,利用 crawl() 方法即可生成一个爬取请求,点击 index_page 链接后面的箭头会出现许多新的爬取请求,即首页所包含的所有链接 此时左边调试区域代码变为: 123456789101112{ \"fetch\": {}, \"process\": { \"callback\": \"index_page\" }, \"project\": \"qunar\", \"schedule\": { \"age\": 864000 }, \"taskid\": \"73a789f99528a2bdc3ab83a13902962a\", \"url\": \"http://travel.qunar.com/travelbook/list.htm\"} callback 变为了 index_page,表示此时执行了 index_page() 方法。传入 index_page() 方法的 response 参数为刚才生成的第一个爬取请求的 response 对象,然后调用 doc() 方法,传入提取所有 a 节点的 CSS 选择器,获取 a 节点的属性 href,实现了页面所有链接的提取,随后遍历所有链接,调用 crawl() 方法,把每个链接构造成新的爬取请求,可以看到 follows 新生成了 229 个爬取请求。点击 web 按钮可以直接预览当前页面,点击 html 按钮可以查看此页面源代码 【17.3.2】信息匹配代码 for each in response.doc('a[href^="http"]').items(): 实现了对整个页面链接的获取,我们需要提取网页的攻略的标题,内容等信息,那么直接替换 doc() 方法里的匹配语句即可,pyspider 提供了非常方便的 CSS 选择器,点击 enable css selector helper 按钮后,选择要匹配的信息并点击,再点击箭头 add to editor 即可得到匹配语句 完成了 CSS 选择器的替换,点击 save 保存,再次点击 run 重新执行 index_page() 方法,可以看到 follows 变为了 10 个,即抓取到了 10 篇攻略 【17.3.3】抓取下一页数据每一页只有 10 篇攻略,想要爬取所有页面的攻略,必须要得到下一页的数据,优化 index_page() 方法: 123456@config(age=10 * 24 * 60 * 60)def index_page(self, response): for each in response.doc('li > .tit > a').items(): self.crawl(each.attr.href, callback=self.detail_page) next = response.doc('.next').attr.href self.crawl(next, callback=self.index_page) 匹配下一页按钮,获取下一页按钮的 URL 并赋值给 next,将该 URL 传给 crawl() 方法,指定回调函数为 index_page() 方法,这样会再次调用 index_page() 方法,提取下一页的攻略标题 【17.3.4】抓取JS渲染数据随便点击一个获取到的攻略,预览该页面,可以观察到头图一直在加载中,切换到 html 查看源代码页面,可以观察到没有 img 节点,那么此处就是后期经过 JavaScript 渲染后才出现的 针对 JavaScript 渲染页面,可以通过 PhantomJS 来实现,具体到 pyspider 中,只需要在 index_page() 的 crawl() 抓取方法中添加一个参数 fetch_type 即可: 123456@config(age=10 * 24 * 60 * 60)def index_page(self, response): for each in response.doc('li > .tit > a').items(): self.crawl(each.attr.href, callback=self.detail_page, fetch_type='js') next = response.doc('.next').attr.href self.crawl(next, callback=self.index_page) 保存之后再次运行即可看到正常页面 【17.3.5】抓取所有数据改写 detail_page() 方法,同样通过 CSS 选择器提取 URL、标题、日期、作者、正文、图片等信息: 1234567891011@config(priority=2)def detail_page(self, response): return { 'url': response.url, 'title': response.doc('#booktitle').text(), 'date': response.doc('.when .data').text(), 'day': response.doc('.howlong .data').text(), 'who': response.doc('.who .data').text(), 'text': response.doc('#b_panel_schedule').text(), 'image': response.doc('.cover_img').attr.src } 【17.3.6】启动爬虫项目该爬虫项目完整代码如下: 12345678910111213141516171819202122232425262728293031323334#!/usr/bin/env python# -*- encoding: utf-8 -*-# Created on 2019-09-18 09:48:29# Project: qunarfrom pyspider.libs.base_handler import *class Handler(BaseHandler): crawl_config = { } @every(minutes=24 * 60) def on_start(self): self.crawl('http://travel.qunar.com/travelbook/list.htm', callback=self.index_page) @config(age=10 * 24 * 60 * 60) def index_page(self, response): for each in response.doc('li > .tit > a').items(): self.crawl(each.attr.href, callback=self.detail_page, fetch_type='js') next = response.doc('.next').attr.href self.crawl(next, callback=self.index_page) @config(priority=2) def detail_page(self, response): return { 'url': response.url, 'title': response.doc('#booktitle').text(), 'date': response.doc('.when .data').text(), 'day': response.doc('.howlong .data').text(), 'who': response.doc('.who .data').text(), 'text': response.doc('#b_panel_schedule').text(), 'image': response.doc('.cover_img').attr.src } 保存代码后,回到主界面,将项目 status 修改为 RUNNING ,点击 actions 的 run 按钮即可启动爬虫 点击 Active Tasks,即可查看最近请求的详细状况: 点击 Results,即可查看所有的爬取结果: 另外,右上角还可以选择 JSON、CSV 格式","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫学习","slug":"Python3-学习笔记/爬虫学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫学习/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"pyspider","slug":"pyspider","permalink":"https://www.itrhx.com/tags/pyspider/"}]},{"title":"Hexo 博客提交百度、谷歌搜索引擎收录","slug":"A48-submit-search-engine-inclusion","date":"2019-09-17T07:59:46.143Z","updated":"2019-12-29T07:20:01.329Z","comments":true,"path":"2019/09/17/A48-submit-search-engine-inclusion/","link":"","permalink":"https://www.itrhx.com/2019/09/17/A48-submit-search-engine-inclusion/","excerpt":"","text":"● 写在前面(必看)网站在没有提交搜索引擎收录之前,直接搜索你网站的内容是搜不到的,只有提交搜索引擎之后,搜索引擎才能收录你的站点,通过爬虫抓取你网站的东西,对于 hexo 博客来说,如果你是部署在 GitHub Pages,那么你是无法被百度收录的,因为 GitHub 禁止了百度爬虫,最常见的解决办法是双线部署到 Coding Pages 和 GitHub Pages,因为百度爬虫可以爬取到 Coding 上的内容,从而实现百度收录,如果你的 hexo 博客还没有实现双线部署,请参考:《Hexo 双线部署到 Coding Pages 和 GitHub Pages 并实现全站 HPPTS》,另外百度收录的所需的时间较长,大约半个月左右才会看到效果! ● 查看网站是否被收录首先我们可以输入 site:域名 来查看域名是否被搜索引擎收录,如下图所示,表示没有收录: ● 百度资源平台添加网站访问百度搜索资源平台官网,注册或者登陆百度账号,依次选择【用户中心】-【站点管理】,添加你的网站,在添加站点时会让你选择协议头(http 或者 https),如果选择 https,它会验证你的站点,大约能在一天之内完成,我的网站已经实现了全站 https,因此选择了 https 协议,但是不知道为什么始终验证失败,实在是无解,只能选择 http 协议了,如果你的站点也实现了全站 https,也可以尝试一下 之后会让你验证网站所有权,提供三种验证方式: 文件验证:下载给定的文件,将其放到本地主题目录 source 文件夹,然后部署上去完成验证 HTML 标签验证:一般是给一个 meta 标签,放到首页 <head> 与 </head> 标签之间即可完成验证 CNAME 验证:个人觉得这种方法最简单,去域名 DNS 添加一个 CNAME 记录即可完成验证 ● 提交百度搜索百度提供了自动提交和手动提交两种方式,其中自动提交又分为主动推送、自动推送和 sitemap 三种方式,以下是官方给出的解释: 主动推送:最为快速的提交方式,推荐您将站点当天新产出链接立即通过此方式推送给百度,以保证新链接可以及时被百度收录 自动推送:是轻量级链接提交组件,将自动推送的 JS 代码放置在站点每一个页面源代码中,当页面被访问时,页面链接会自动推送给百度,有利于新页面更快被百度发现 sitemap:您可以定期将网站链接放到sitemap中,然后将sitemap提交给百度。百度会周期性的抓取检查您提交的sitemap,对其中的链接进行处理,但收录速度慢于主动推送 手动提交:如果您不想通过程序提交,那么可以采用此种方式,手动将链接提交给百度 四种提交方式对比: 方式 主动推送 自动推送 Sitemap 手动提交 速度 最快 —— —— —— 开发成本 高 低 中 不需开发 可提交量 低 高 高 低 是否建议提交历史连接 否 是 是 是 和其他提交方法是否有冲突 无 无 无 无 个人推荐同时使用主动推送和 sitemap 方式,下面将逐一介绍这四种提交方式的具体实现方法 ● 主动推送在博客根目录安装插件 npm install hexo-baidu-url-submit --save,然后在根目录 _config.yml 文件里写入以下配置: 12345baidu_url_submit: count: 1 # 提交最新的多少个链接 host: www.itrhx.com # 在百度站长平台中添加的域名 token: your_token # 秘钥 path: baidu_urls.txt # 文本文档的地址, 新链接会保存在此文本文档里 其中的 token 可以在【链接提交】-【自动提交】-【主动推送】下面看到,接口调用地址最后面 token=xxxxx 即为你的 token 同样是在根目录的 _config.yml 文件,大约第 17 行处,url 要改为在百度站长平台添加的域名,也就是你网站的首页地址: 1234# URLurl: https://www.itrhx.comroot: /permalink: :year/:month/:day/:title/ 最后,加入新的 deployer: 123456789# Deployment## Docs: https://hexo.io/docs/deployment.htmldeploy:- type: git repository: github: git@github.com:TRHX/TRHX.github.io.git # 这是原来的 github 配置 coding: git@git.dev.tencent.com:TRHX/TRHX.git # 这是原来的 coding 配置 branch: master- type: baidu_url_submitter # 这是新加的主动推送 最后执行 hexo g -d 部署一遍即可实现主动推送,推送成功的标志是:在执行部署命令最后会显示类似如下代码: 12{\"remain\":4999953,\"success\":47}INFO Deploy done: baidu_url_submitter 这表示有 47 个页面已经主动推送成功,remain 的意思是当天剩余的可推送 url 条数 主动推送相关原理介绍: 新链接的产生:hexo generate 会产生一个文本文件,里面包含最新的链接 新链接的提交:hexo deploy 会从上述文件中读取链接,提交至百度搜索引擎 该插件的 GitHub 地址:https://github.com/huiwang/hexo-baidu-url-submit ● 自动推送关于自动推送百度官网给出的解释是:自动推送是百度搜索资源平台为提高站点新增网页发现速度推出的工具,安装自动推送JS代码的网页,在页面被访问时,页面URL将立即被推送给百度 此时要注意,有些 hexo 主题集成了这项功能,比如 next 主题,在 themes\\next\\layout_scripts\\ 下有个 baidu_push.swig 文件,我们只需要把如下代码粘贴到该文件,然后在主题配置文件设置 baidu_push: true 即可 12345678910111213141516{% if theme.baidu_push %}<script>(function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName(\"script\")[0]; s.parentNode.insertBefore(bp, s);})();</script>{% endif %} 然而大部分主题是没有集成这项功能的,对于大部分主题来说,我们可以把以下代码粘贴到 head.ejs 文件的 <head> 与 </head> 标签之间即可,从而实现自动推送(比如我使用的是 Material X 主题,那么只需要把代码粘贴到 \\themes\\material-x\\layout\\_partial\\head.ejs 中即可) 1234567891011121314<script>(function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName(\"script\")[0]; s.parentNode.insertBefore(bp, s);})();</script> ● sitemap首先我们要使用以下命令生成一个网站地图: 12npm install hexo-generator-sitemap --save npm install hexo-generator-baidu-sitemap --save 这里也注意一下,将根目录的 _config.yml 文件,大约第 17 行处,url 改为在百度站长平台添加的域名,也就是你网站的首页地址: 1234# URLurl: https://www.itrhx.comroot: /permalink: :year/:month/:day/:title/ 然后使用命令 hexo g -d 将网站部署上去,然后访问 你的首页/sitemap.xml 或者 你的首页/baidusitemap.xml 就可以看到网站地图了 比如我的是:https://www.itrhx.com/baidusitemap.xml 或者 https://www.itrhx.com/sitemap.xml 其中 sitemap.xml 文件是搜索引擎通用的 sitemap 文件,baidusitemap.xml 是百度专用的 sitemap 文件 然后来到百度站长平台的 sitemap 提交页面,将你的 sitemap 地址提交即可,如果成功的话状态会显示为正常,初次提交要等几分钟,sitemap.xml 相比 baidusitemap.xml 来说等待时间也会更长,如果以后你博客有新的文章或其他页面,可以点击手动更新文件,更新一下新的 sitemap ● 手动提交手动提交不需要其他额外操作,直接把需要收录的页面的 url 提交即可,这种方法效率较低,更新较慢,不推荐使用 ● 提交谷歌搜索提交谷歌搜索引擎比较简单,在提交之前,我们依然可以使用 site:域名 查看网站是否被收录,我的网站搭建了有差不多一年了,之前也没提交过收录,不过谷歌爬虫的确是强大,即使没有提交过,现在也能看到有一百多条结果了: 接下来我们将网站提交谷歌搜索引擎搜索,进入谷歌站长平台,登录你的谷歌账号之后会让你验证网站所有权: 有两种验证方式,分别是网域和网址前缀,两种资源类型区别如下: 网址前缀资源 网域资源 说明 仅包含具有指定前缀(包括协议 http/https)的网址。如果希望资源匹配任何协议或子网域(http/https/www./m. 等),建议改为添加网域资源。 包括所有子网域(m、www 等)和多种协议(http、https、ftp)的网域级资源。 验证 多种类型 仅 DNS 记录验证 示例 资源 http://example.com/✔ http://example.com/dresses/1234X https://example.com/dresses/1234X http://www.example.com/dresses/1234 资源 example.com✔ http://example.com/dresses/1234✔ https://example.com/dresses/1234✔ http://www.example.com/dresses/1234✔ http://support.m.example.com/dresses/1234 由对比可知选择网域资源验证方式比较好,只需要一个域名就可以匹配到多种格式的 URL,之后会给你一个 TXT 的记录值,复制它到你域名 DNS 增加一个 TXT 记录,点击验证即可 提交谷歌收录比较简单,选择站点地图,将我们之前生成的 sitemap 提交就行了,过几分钟刷新一下看到成功字样表示提交成功!","categories":[{"name":"Hexo","slug":"Hexo","permalink":"https://www.itrhx.com/categories/Hexo/"}],"tags":[{"name":"Hexo","slug":"Hexo","permalink":"https://www.itrhx.com/tags/Hexo/"},{"name":"SEO","slug":"SEO","permalink":"https://www.itrhx.com/tags/SEO/"}]},{"title":"Hexo 双线部署到 Coding Pages 和 GitHub Pages 并实现全站 HTTPS","slug":"A47-hexo-deployed-to-github-and-coding","date":"2019-09-16T06:11:40.959Z","updated":"2020-04-06T12:04:00.052Z","comments":true,"path":"2019/09/16/A47-hexo-deployed-to-github-and-coding/","link":"","permalink":"https://www.itrhx.com/2019/09/16/A47-hexo-deployed-to-github-and-coding/","excerpt":"","text":"部署到 Coding Pages 的好处:国内访问速度更快,可以提交百度收录(GitHub 禁止了百度的爬取) 部署到 Coding Pages 的坏处:就今年来说,Coding 不太稳定,随时有宕机的可能,群里的朋友已经经历过几次了,不过相信以后会越来越稳定的 部署过程中常见的问题:无法实现全站 HTTPS,Coding 申请 SSL 证书失败,浏览器可能会提示不是安全链接 本文前提:你已经将 Hexo 成功部署到了 GitHub Pages,如果还没有,请参考:《使用Github Pages和Hexo搭建自己的独立博客【超级详细的小白教程】》 本文将全面讲述如何成功双线部署到 Coding Pages 和 GitHub Pages 并实现全站 HPPTS,同时解决一些常见的问题! 1.创建项目进入 Coding 官网,点击个人版登陆,没有账号就注册一个并登录,由于 Coding 已经被腾讯收购了,所以登录就会来到腾讯云开发者平台,点击创建项目 项目名称建议和你的用户名一致,这样做的好处是:到时候可以直接通过 user_name.coding.me 访问你的博客,如果项目名与用户名不一致,则需要通过 user_name.coding.me/project_name 才能访问,项目描述可以随便写 2.配置公钥配置 SSH 公钥方法与 GitHub Pages 的方式差不多,点击你的头像,依次选择【个人设置】-【SSH公钥】-【新增公钥】 前面部署到 GitHub Pages 的时候就已经有了一对公钥,我们直接将该公钥粘贴进去就行,公钥名称可以随便写,选中永久有效选项 PS:公钥储存位置一般在 C:\\Users\\用户名\\.ssh 目录下的 id_rsa.pub 文件里,用记事本打开复制其内容即可 添加公钥后,我们可以右键 Get Bash,输入以下命令来检查是否配置成功: 1ssh -T git@e.coding.net 若出现以下提示,则证明配置成功: 12Coding 提示: Hello XXX, You've connected to Coding.net via SSH. This is a personal key.XXX,你好,你已经通过 SSH 协议认证 Coding.net 服务,这是一个个人公钥 3.配置 _config.yml进入你的项目,在右下角有选择连接方式,选择 SSH 方式(HTTPS 方式也可以,但是这种方式有时候可能连接不上,SSH 连接不容易出问题),一键复制,然后打开你本地博客根目录的 _config.yml 文件,找到 deploy 关键字,添加 coding 地址:coding: git@git.dev.tencent.com:user_name/user_name.git,也就是刚刚复制的 SSH 地址。 【2020.04.06 更新】coding 地址格式现在有所改变,类似于 `git@e.coding.net:TRHX/TRHX.git`,记住去仓库复制你自己的即可。 添加完成后先执行命令 hexo clean 清理一下缓存,然后执行命令 hexo g -d 将博客双线部署到 Coding Pages 和 GitHub Pages,如下图所示表示部署成功: 4.开启 Coding Pages进入你的项目,在代码栏下选择 Pages 服务,一键开启 Coding Pages,等待几秒后刷新网页即可看到已经开启的 Coding Pages,到目前为止,你就可以通过 xxxx.coding.me(比如我的是 trhx.coding.me)访问你的 Coding Pages 页面了 【2020.04.06 更新】coding 分配的域名现在有所改变,类似于 https://p51l67.coding-pages.com 5.绑定域名并开启 HPPTS首先在你的域名 DNS 设置中添加一条 CNAME 记录指向 xxxx.coding.me,解析路线选择 默认,将 GitHub 的解析路线改为 境外,这样境外访问就会走 GitHub,境内就会走 Coding,也有人说阿里云是智能解析,自动分配路线,如果解析路线都是默认,境外访问同样会智能选择走 GitHub,境内走 Coding,我没有验证过,有兴趣的可以自己试试,我的解析如下图所示: 【2020.04.06 更新】coding 分配的域名现在有所改变,类似于 https://p51l67.coding-pages.com,请注意解析当中记录值的填写。 然后点击静态 Pages 应用右上角的设置,进入设置页面,这里要注意,如果你之前已经部署到了 GitHub Pages 并开启了 HTTPS,那么直接在设置页面绑定你自己的域名,SSL/TLS 安全证书就会显示申请错误,如下图所示,没有申请到 SSL 证书,当你访问你的网站时,浏览器就会提示不是安全连接 申请错误原因是:在验证域名所有权时会定位到 Github Pages 的主机上导致 SSL 证书申请失败 正确的做法是:先去域名 DNS 把 GitHub 的解析暂停掉,然后再重新申请 SSL 证书,大约十秒左右就能申请成功,然后开启强制 HTTPS 访问 这里也建议同时绑定有 www 前缀和没有 www 前缀的,如果要绑定没有 www 前缀的,首先要去域名 DNS 添加一个 A 记录,主机记录为 @,记录值为你博客 IP 地址,IP 地址可以在 cmd 命令行 ping 一下得到,然后在 Coding Pages 中设置其中一个为【首选】,另一个设置【跳转至首选】,这样不管用户是否输入 www 前缀都会跳到有 www 前缀的了 在博客资源引用的时候也要注意所有资源的 URL 必须是以 https:// 开头,不然浏览器依旧会提示不安全! 至此,我们的 Hexo 博客就成功双线部署到 Coding Pages 和 GitHub Pages 了,并且也实现了全站 HPPTS,最后来一张 GitHub Pages 和 Coding Pages 在国内的速度对比图,可以明显看到速度的提升","categories":[{"name":"Hexo","slug":"Hexo","permalink":"https://www.itrhx.com/categories/Hexo/"}],"tags":[{"name":"Hexo","slug":"Hexo","permalink":"https://www.itrhx.com/tags/Hexo/"},{"name":"Coding Pages","slug":"Coding-Pages","permalink":"https://www.itrhx.com/tags/Coding-Pages/"},{"name":"GitHub Pages","slug":"GitHub-Pages","permalink":"https://www.itrhx.com/tags/GitHub-Pages/"}]},{"title":"Python3 爬虫学习笔记 C16","slug":"A46-Python3-spider-C16","date":"2019-09-13T16:44:50.577Z","updated":"2019-09-24T12:43:19.863Z","comments":true,"path":"2019/09/14/A46-Python3-spider-C16/","link":"","permalink":"https://www.itrhx.com/2019/09/14/A46-Python3-spider-C16/","excerpt":"Python3 爬虫学习笔记第十六章 —— 【数据储存系列 — Redis】","text":"Python3 爬虫学习笔记第十六章 —— 【数据储存系列 — Redis】 【16.1】关于 RedisRedis 是一个基于内存的高效的键值型(key-value)非关系型数据库,它支持存储的 value 类型非常多,包括 string(字符串)、list(链表)、set(集合)、zset(sorted set –有序集合) 和 hash(哈希类型),它的性能十分优越,可以支持每秒十几万此的读/写操作,其性能远超数据库,并且还支持集群、分布式、主从同步等配置,原则上可以无限扩展,让更多的数据存储在内存中,此外,它还支持一定的事务能力,这保证了高并发的场景下数据的安全和一致性。 【16.2】使用 Redis首先安装 Redis 和 redis-py 库,管理 Redis 可以使用可视化工具 Redis Desktop Manager,该工具现在收费了,分享个 0.8.8.384 的免费版本 安装 redis-py 库:pip install redisRedis 官网:https://redis.io官方文档:https://redis.io/documentation中文官网:http://www.redis.cn中文教程:http://www.runoob.com/redis/redis-tutorial.htmlGitHub:https://github.com/antirez/redisRedis Windows下载地址一:https://github.com/microsoftarchive/redis/releases (最新版 3.2.100,似乎不再更新)Redis Windows下载地址二:https://github.com/tporadowski/redis/releases (最新版)Redis Desktop Manager 官网:https://redisdesktop.com/Redis Desktop Manager 0.8.8.384 免费版:https://pan.baidu.com/s/18MKeCqT0MG0hc89jfkpIkA (提取码:3ovc) 利用 Python 连接 Redis 示例: 12345from redis import StrictRedisredis = StrictRedis(host='localhost', port=6379, db=0, password='000000')redis.set('name', 'TRHX')print(redis.get('name')) 传入 Redis 的地址、运行端口、使用的数据库和密码, 4 个参数默认值分别为 localhost、6379、0 和 None,声明一个 StrictRedis 对象,调用 set() 方法,设置一个键值对,输出结果如下: 1b'TRHX' 另外也可以使用 ConnectionPool 来连接: 1234from redis import StrictRedis, ConnectionPool pool = ConnectionPool(host='localhost', port=6379, db=0, password='000000') redis = StrictRedis(connection_pool=pool) ConnectionPool 也支持通过 URL 来构建: 123redis://[:password]@host:port/db # 创建 Redis TCP 连接rediss://[:password]@host:port/db # 创建 Redis TCP+SSL 连接unix://[:password]@/path/to/socket.sock?db=db # 创建 Redis UNIX socket 连接 代码示例: 12345from redis import StrictRedis, ConnectionPoolurl = 'redis://:000000@localhost:6379/0' pool = ConnectionPool.from_url(url) redis = StrictRedis(connection_pool=pool) 以下是有关的键操作、字符串操作、列表操作、集合操作、散列操作的各种方法,记录一下,方便查阅来源:《Python3 网络爬虫开发实战(崔庆才著)》Redis 命令参考:http://redisdoc.com/ 、http://doc.redisfans.com/ 【16.3】Key(键)操作 方法 作用 参数说明 示例 示例说明 示例结果 exists(name) 判断一个键是否存在 name:键名 redis.exists(‘name’) 是否存在 name 这个键 True delete(name) 删除一个键 name:键名 redis.delete(‘name’) 删除 name 这个键 1 type(name) 判断键类型 name:键名 redis.type(‘name’) 判断 name 这个键类型 b’string’ keys(pattern) 获取所有符合规则的键 pattern:匹配规则 redis.keys(‘n*’) 获取所有以 n 开头的键 [b’name’] randomkey() 获取随机的一个键 randomkey() 获取随机的一个键 b’name’ rename(src, dst) 重命名键 src:原键名;dst:新键名 redis.rename(‘name’, ‘nickname’) 将 name 重命名为 nickname True dbsize() 获取当前数据库中键的数目 dbsize() 获取当前数据库中键的数目 100 expire(name, time) 设定键的过期时间,单位为秒 name:键名;time:秒数 redis.expire(‘name’, 2) 将 name 键的过期时间设置为 2 秒 True ttl(name) 获取键的过期时间,单位为秒,-1 表示永久不过期 name:键名 redis.ttl(‘name’) 获取 name 这个键的过期时间 -1 move(name, db) 将键移动到其他数据库 name:键名;db:数据库代号 move(‘name’, 2) 将 name 移动到 2 号数据库 True flushdb() 删除当前选择数据库中的所有键 flushdb() 删除当前选择数据库中的所有键 True flushall() 删除所有数据库中的所有键 flushall() 删除所有数据库中的所有键 True 【16.4】String(字符串)操作 方法 作用 参数说明 示例 示例说明 示例结果 set(name, value) 给数据库中键名为 name 的 string 赋予值 value name:键名;value:值 redis.set(‘name’, ‘Bob’) 给 name 这个键的 value 赋值为 Bob True get(name) 返回数据库中键名为 name 的 string 的 value name:键名 redis.get(‘name’) 返回 name 这个键的 value b’Bob’ getset(name, value) 给数据库中键名为 name 的 string 赋予值 value 并返回上次的 value name:键名;value:新值 redis.getset(‘name’, ‘Mike’) 赋值 name 为 Mike 并得到上次的 value b’Bob’ mget(keys, *args) 返回多个键对应的 value 组成的列表 keys:键名序列 redis.mget([‘name’, ‘nickname’]) 返回 name 和 nickname 的 value [b’Mike’, b’Miker’] setnx(name, value) 如果不存在这个键值对,则更新 value,否则不变 name:键名 redis.setnx(‘newname’, ‘James’) 如果 newname 这个键不存在,则设置值为 James 第一次运行结果是 True,第二次运行结果是 False setex(name, time, value) 设置可以对应的值为 string 类型的 value,并指定此键值对应的有效期 name:键名;time:有效期;value:值 redis.setex(‘name’, 1, ‘James’) 将 name 这个键的值设为 James,有效期为 1 秒 True setrange(name, offset, value) 设置指定键的 value 值的子字符串 name:键名;offset:偏移量;value:值 redis.set(‘name’, ‘Hello’) redis.setrange (‘name’, 6, ‘World’) 设置 name 为 Hello 字符串,并在 index 为 6 的位置补 World 11,修改后的字符串长度 mset(mapping) 批量赋值 mapping:字典或关键字参数 redis.mset({‘name1’: ‘Durant’, ‘name2’: ‘James’}) 将 name1 设为 Durant,name2 设为 James True msetnx(mapping) 键均不存在时才批量赋值 mapping:字典或关键字参数 redis.msetnx({‘name3’: ‘Smith’, ‘name4’: ‘Curry’}) 在 name3 和 name4 均不存在的情况下才设置二者值 True incr(name, amount=1) 键名为 name 的 value 增值操作,默认为 1,键不存在则被创建并设为 amount name:键名;amount:增长的值 redis.incr(‘age’, 1) age 对应的值增 1,若不存在,则会创建并设置为 1 1,即修改后的值 decr(name, amount=1) 键名为 name 的 value 减值操作,默认为 1,键不存在则被创建并将 value 设置为 - amount name:键名;amount:减少的值 redis.decr(‘age’, 1) age 对应的值减 1,若不存在,则会创建并设置为-1 -1,即修改后的值 append(key, value) 键名为 key 的 string 的值附加 value key:键名 redis.append(‘nickname’, ‘OK’) 向键名为 nickname 的值后追加 OK 13,即修改后的字符串长度 substr(name, start, end=-1) 返回键名为 name 的 string 的子字符串 name:键名;start:起始索引;end:终止索引,默认为-1,表示截取到末尾 redis.substr(‘name’, 1, 4) 返回键名为 name 的值的字符串,截取索引为 1~4 的字符 b’ello’ getrange(key, start, end) 获取键的 value 值从 start 到 end 的子字符串 key:键名;start:起始索引;end:终止索引 redis.getrange(‘name’, 1, 4) 返回键名为 name 的值的字符串,截取索引为 1~4 的字符 b’ello 【16.5】Hash(哈希表)操作 方法 作用 参数说明 示例 示例说明 示例结果 hset(name, key, value) 向键名为 name 的散列表中添加映射 name:键名;key:映射键名;value:映射键值 hset(‘price’, ‘cake’, 5) 向键名为 price 的散列表中添加映射关系,cake 的值为 5 1,即添加的映射个数 hsetnx(name, key, value) 如果映射键名不存在,则向键名为 name 的散列表中添加映射 name:键名;key:映射键名;value:映射键值 hsetnx(‘price’, ‘book’, 6) 向键名为 price 的散列表中添加映射关系,book 的值为 6 1,即添加的映射个数 hget(name, key) 返回键名为 name 的散列表中 key 对应的值 name:键名;key:映射键名 redis.hget(‘price’, ‘cake’) 获取键名为 price 的散列表中键名为 cake 的值 5 hmget(name, keys, *args) 返回键名为 name 的散列表中各个键对应的值 name:键名;keys:键名序列 redis.hmget(‘price’, [‘apple’, ‘orange’]) 获取键名为 price 的散列表中 apple 和 orange 的值 [b’3’, b’7’] hmset(name, mapping) 向键名为 name 的散列表中批量添加映射 name:键名;mapping:映射字典 redis.hmset(‘price’, {‘banana’: 2, ‘pear’: 6}) 向键名为 price 的散列表中批量添加映射 True hincrby(name, key, amount=1) 将键名为 name 的散列表中映射的值增加 amount name:键名;key:映射键名;amount:增长量 redis.hincrby(‘price’, ‘apple’, 3) key 为 price 的散列表中 apple 的值增加 3 6,修改后的值 hexists(name, key) 键名为 name 的散列表中是否存在键名为键的映射 name:键名;key:映射键名 redis.hexists(‘price’, ‘banana’) 键名为 price 的散列表中 banana 的值是否存在 True hdel(name, *keys) 在键名为 name 的散列表中,删除键名为键的映射 name:键名;keys:键名序列 redis.hdel(‘price’, ‘banana’) 从键名为 price 的散列表中删除键名为 banana 的映射 True hlen(name) 从键名为 name 的散列表中获取映射个数 name:键名 redis.hlen(‘price’) 从键名为 price 的散列表中获取映射个数 6 hkeys(name) 从键名为 name 的散列表中获取所有映射键名 name:键名 redis.hkeys(‘price’) 从键名为 price 的散列表中获取所有映射键名 [b’cake’, b’book’, b’banana’, b’pear’] hvals(name) 从键名为 name 的散列表中获取所有映射键值 name:键名 redis.hvals(‘price’) 从键名为 price 的散列表中获取所有映射键值 [b’5’, b’6’, b’2’, b’6’] hgetall(name) 从键名为 name 的散列表中获取所有映射键值对 name:键名 redis.hgetall(‘price’) 从键名为 price 的散列表中获取所有映射键值对 {b’cake’: b’5’, b’book’: b’6’, b’orange’: b’7’, b’pear’: b’6’} 【16.6】List(列表)操作 方法 作用 参数说明 示例 示例说明 示例结果 rpush(name, *values) 在键名为 name 的列表末尾添加值为 value 的元素,可以传多个 name:键名;values:值 redis.rpush(‘list’, 1, 2, 3) 向键名为 list 的列表尾添加 1、2、3 3,列表大小 lpush(name, *values) 在键名为 name 的列表头添加值为 value 的元素,可以传多个 name:键名;values:值 redis.lpush(‘list’, 0) 向键名为 list 的列表头部添加 0 4,列表大小 llen(name) 返回键名为 name 的列表的长度 name:键名 redis.llen(‘list’) 返回键名为 list 的列表的长度 4 lrange(name, start, end) 返回键名为 name 的列表中 start 至 end 之间的元素 name:键名;start:起始索引;end:终止索引 redis.lrange(‘list’, 1, 3) 返回起始索引为 1 终止索引为 3 的索引范围对应的列表 [b’3’, b’2’, b’1’] ltrim(name, start, end) 截取键名为 name 的列表,保留索引为 start 到 end 的内容 name:键名;start:起始索引;end:终止索引 ltrim(‘list’, 1, 3) 保留键名为 list 的索引为 1 到 3 的元素 True lindex(name, index) 返回键名为 name 的列表中 index 位置的元素 name:键名;index:索引 redis.lindex(‘list’, 1) 返回键名为 list 的列表索引为 1 的元素 b’2’ lset(name, index, value) 给键名为 name 的列表中 index 位置的元素赋值,越界则报错 name:键名;index:索引位置;value:值 redis.lset(‘list’, 1, 5) 将键名为 list 的列表中索引为 1 的位置赋值为 5 True lrem(name, count, value) 删除 count 个键的列表中值为 value 的元素 name:键名;count:删除个数;value:值 redis.lrem(‘list’, 2, 3) 将键名为 list 的列表删除两个 3 1,即删除的个数 lpop(name) 返回并删除键名为 name 的列表中的首元素 name:键名 redis.lpop(‘list’) 返回并删除名为 list 的列表中的第一个元素 b’5’ rpop(name) 返回并删除键名为 name 的列表中的尾元素 name:键名 redis.rpop(‘list’) 返回并删除名为 list 的列表中的最后一个元素 b’2’ blpop(keys, timeout=0) 返回并删除名称在 keys 中的 list 中的首个元素,如果列表为空,则会一直阻塞等待 keys:键名序列;timeout:超时等待时间,0 为一直等待 redis.blpop(‘list’) 返回并删除键名为 list 的列表中的第一个元素 [b’5’] brpop(keys, timeout=0) 返回并删除键名为 name 的列表中的尾元素,如果 list 为空,则会一直阻塞等待 keys:键名序列;timeout:超时等待时间,0 为一直等待 redis.brpop(‘list’) 返回并删除名为 list 的列表中的最后一个元素 [b’2’] rpoplpush(src, dst) 返回并删除名称为 src 的列表的尾元素,并将该元素添加到名称为 dst 的列表头部 src:源列表的键;dst:目标列表的 key redis.rpoplpush(‘list’, ‘list2’) 将键名为 list 的列表尾元素删除并将其添加到键名为 list2 的列表头部,然后返回 b’2’ 【16.7】Set(集合)操作 方法 作用 参数说明 示例 示例说明 示例结果 sadd(name, *values) 向键名为 name 的集合中添加元素 name:键名;values:值,可为多个 redis.sadd(‘tags’, ‘Book’, ‘Tea’, ‘Coffee’) 向键名为 tags 的集合中添加 Book、Tea 和 Coffee 这 3 个内容 3,即插入的数据个数 srem(name, *values) 从键名为 name 的集合中删除元素 name:键名;values:值,可为多个 redis.srem(‘tags’, ‘Book’) 从键名为 tags 的集合中删除 Book 1,即删除的数据个数 spop(name) 随机返回并删除键名为 name 的集合中的一个元素 name:键名 redis.spop(‘tags’) 从键名为 tags 的集合中随机删除并返回该元素 b’Tea’ smove(src, dst, value) 从 src 对应的集合中移除元素并将其添加到 dst 对应的集合中 src:源集合;dst:目标集合;value:元素值 redis.smove(‘tags’, ‘tags2’, ‘Coffee’) 从键名为 tags 的集合中删除元素 Coffee 并将其添加到键为 tags2 的集合 True scard(name) 返回键名为 name 的集合的元素个数 name:键名 redis.scard(‘tags’) 获取键名为 tags 的集合中的元素个数 3 sismember(name, value) 测试 member 是否是键名为 name 的集合的元素 name:键值 redis.sismember(‘tags’, ‘Book’) 判断 Book 是否是键名为 tags 的集合元素 True sinter(keys, *args) 返回所有给定键的集合的交集 keys:键名序列 redis.sinter([‘tags’, ‘tags2’]) 返回键名为 tags 的集合和键名为 tags2 的集合的交集 {b’Coffee’} sinterstore(dest, keys, *args) 求交集并将交集保存到 dest 的集合 dest:结果集合;keys:键名序列 redis.sinterstore (‘inttag’, [‘tags’, ‘tags2’]) 求键名为 tags 的集合和键名为 tags2 的集合的交集并将其保存为 inttag 1 sunion(keys, *args) 返回所有给定键的集合的并集 keys:键名序列 redis.sunion([‘tags’, ‘tags2’]) 返回键名为 tags 的集合和键名为 tags2 的集合的并集 {b’Coffee’, b’Book’, b’Pen’} sunionstore(dest, keys, *args) 求并集并将并集保存到 dest 的集合 dest:结果集合;keys:键名序列 redis.sunionstore (‘inttag’, [‘tags’, ‘tags2’]) 求键名为 tags 的集合和键名为 tags2 的集合的并集并将其保存为 inttag 3 sdiff(keys, *args) 返回所有给定键的集合的差集 keys:键名序列 redis.sdiff([‘tags’, ‘tags2’]) 返回键名为 tags 的集合和键名为 tags2 的集合的差集 {b’Book’, b’Pen’} sdiffstore(dest, keys, *args) 求差集并将差集保存到 dest 集合 dest:结果集合;keys:键名序列 redis.sdiffstore (‘inttag’, [‘tags’, ‘tags2’]) 求键名为 tags 的集合和键名为 tags2 的集合的差集并将其保存为 inttag 3 smembers(name) 返回键名为 name 的集合的所有元素 name:键名 redis.smembers(‘tags’) 返回键名为 tags 的集合的所有元素 {b’Pen’, b’Book’, b’Coffee’} srandmember(name) 随机返回键名为 name 的集合中的一个元素,但不删除元素 name:键值 redis.srandmember(‘tags’) 随机返回键名为 tags 的集合中的一个元素 Srandmember (name) 【16.8】SortedSet(有序集合)操作 方法 作用 参数说明 示例 示例说明 示例结果 zadd(name, args, *kwargs) 向键名为 name 的 zset 中添加元素 member,score 用于排序。如果该元素存在,则更新其顺序 name:键名;args:可变参数 redis.zadd(‘grade’, 100, ‘Bob’, 98, ‘Mike’) 向键名为 grade 的 zset 中添加 Bob(其 score 为 100),并添加 Mike(其 score 为 98) 2,即添加的元素个数 zrem(name, *values) 删除键名为 name 的 zset 中的元素 name:键名;values:元素 redis.zrem(‘grade’, ‘Mike’) 从键名为 grade 的 zset 中删除 Mike 1,即删除的元素个数 zincrby(name, value, amount=1) 如果在键名为 name 的 zset 中已经存在元素 value,则将该元素的 score 增加 amount;否则向该集合中添加该元素,其 score 的值为 amount name:键名;value:元素;amount:增长的 score 值 redis.zincrby(‘grade’, ‘Bob’, -2) 键名为 grade 的 zset 中 Bob 的 score 减 2 98.0,即修改后的值 zrank(name, value) 返回键名为 name 的 zset 中元素的排名,按 score 从小到大排序,即名次 name:键名;value:元素值 redis.zrank(‘grade’, ‘Amy’) 得到键名为 grade 的 zset 中 Amy 的排名 1 zrevrank(name, value) 返回键为 name 的 zset 中元素的倒数排名(按 score 从大到小排序),即名次 name:键名;value:元素值 redis.zrevrank (‘grade’, ‘Amy’) 得到键名为 grade 的 zset 中 Amy 的倒数排名 2 zrevrange(name, start, end, withscores= False) 返回键名为 name 的 zset(按 score 从大到小排序)中 index 从 start 到 end 的所有元素 name:键值;start:开始索引;end:结束索引;withscores:是否带 score redis.zrevrange (‘grade’, 0, 3) 返回键名为 grade 的 zset 中前四名元素 [b’Bob’, b’Mike’, b’Amy’, b’James’] zrangebyscore (name, min, max, start=None, num=None, withscores=False) 返回键名为 name 的 zset 中 score 在给定区间的元素 name:键名;min:最低 score;max:最高 score;start:起始索引;num:个数;withscores:是否带 score redis.zrangebyscore (‘grade’, 80, 95) 返回键名为 grade 的 zset 中 score 在 80 和 95 之间的元素 [b’Bob’, b’Mike’, b’Amy’, b’James’] zcount(name, min, max) 返回键名为 name 的 zset 中 score 在给定区间的数量 name:键名;min:最低 score;max:最高 score redis.zcount(‘grade’, 80, 95) 返回键名为 grade 的 zset 中 score 在 80 到 95 的元素个数 2 zcard(name) 返回键名为 name 的 zset 的元素个数 name:键名 redis.zcard(‘grade’) 获取键名为 grade 的 zset 中元素的个数 3 zremrangebyrank (name, min, max) 删除键名为 name 的 zset 中排名在给定区间的元素 name:键名;min:最低位次;max:最高位次 redis.zremrangebyrank (‘grade’, 0, 0) 删除键名为 grade 的 zset 中排名第一的元素 1,即删除的元素个数 zremrangebyscore (name, min, max) 删除键名为 name 的 zset 中 score 在给定区间的元素 name:键名;min:最低 score;max:最高 score redis.zremrangebyscore (‘grade’, 80, 90) 删除 score 在 80 到 90 之间的元素 1,即删除的元素个数 【16.9】RedisDumpRedisDump 是 Redis 一个数据导入导出工具,是基于 Ruby 实现的,首先访问 Ruby 官网安装对应操作系统的 Ruby:http://www.ruby-lang.org/zh_cn/downloads/ ,安装完成即可使用 gem 命令,该命令类似于 Python 当中的 pip 命令,使用 gem install redis-dump 即可完成 RedisDump 的安装,安装完成后就可以使用导出数据 redis-dump 命令和导入数据 redis-load 命令了 【16.9.1】导出数据 redis-dump在命令行输入 redis-dump -h 可以查看: 123456789101112Usage: E:/Ruby26-x64/bin/redis-dump [global options] COMMAND [command options] -u, --uri=S Redis URI (e.g. redis://hostname[:port]) -d, --database=S Redis database (e.g. -d 15) -a, --password=S Redis password (e.g. -a 'my@pass/word') -s, --sleep=S Sleep for S seconds after dumping (for debugging) -c, --count=S Chunk size (default: 10000) -f, --filter=S Filter selected keys (passed directly to redis' KEYS command) -b, --base64 Encode key values as base64 (useful for binary values) -O, --without_optimizations Disable run time optimizations -V, --version Display version -D, --debug --nosafe 命令解释: -u Redis 连接字符串 -d 数据库代号 -a 数据库密码 -s 导出之后的休眠时间 -c 分块大小,默认是 10000 -f 导出时的过滤器 -b 将键值编码为 base64(对二进制值有用) -O 禁用运行时优化 -V 显示版本 -D 开启调试 导出数据示例: 12345678910111213redis-dump# 指定端口redis-dump -u 127.0.0.1:6379# 指定端口和密码redis-dump -u :password@127.0.0.1:6379# 导出指定数据库redis-dump -u 127.0.0.1:6379 -d 3# 导出包含特定值的数据redis-dump -u 127.0.0.1:6379 -f age 输出示例: 1234567891011121314151617181920212223# 导出所有数据{\"db\":0,\"key\":\"name5\",\"ttl\":-1,\"type\":\"string\",\"value\":\"DDD\",\"size\":3}{\"db\":0,\"key\":\"name2\",\"ttl\":-1,\"type\":\"string\",\"value\":\"AAA\",\"size\":3}{\"db\":0,\"key\":\"name4\",\"ttl\":-1,\"type\":\"string\",\"value\":\"CCC\",\"size\":3}{\"db\":0,\"key\":\"name6\",\"ttl\":-1,\"type\":\"string\",\"value\":\"CCC\",\"size\":3}{\"db\":0,\"key\":\"name\",\"ttl\":-1,\"type\":\"string\",\"value\":\"TRHX\",\"size\":4}{\"db\":0,\"key\":\"name3\",\"ttl\":-1,\"type\":\"string\",\"value\":\"BBB\",\"size\":3}{\"db\":1,\"key\":\"name2\",\"ttl\":-1,\"type\":\"string\",\"value\":\"BBB\",\"size\":3}{\"db\":1,\"key\":\"name1\",\"ttl\":-1,\"type\":\"string\",\"value\":\"AAA\",\"size\":3}{\"db\":2,\"key\":\"name2\",\"ttl\":-1,\"type\":\"string\",\"value\":\"BBB\",\"size\":3}{\"db\":2,\"key\":\"name1\",\"ttl\":-1,\"type\":\"string\",\"value\":\"AAA\",\"size\":3}{\"db\":3,\"key\":\"name2\",\"ttl\":-1,\"type\":\"string\",\"value\":\"HHH\",\"size\":3}{\"db\":3,\"key\":\"name1\",\"ttl\":-1,\"type\":\"string\",\"value\":\"RRR\",\"size\":3}{\"db\":4,\"key\":\"age\",\"ttl\":-1,\"type\":\"string\",\"value\":\"20\",\"size\":2}{\"db\":4,\"key\":\"age2\",\"ttl\":-1,\"type\":\"string\",\"value\":\"19\",\"size\":2}# 导出 3 号数据库{\"db\":3,\"key\":\"name2\",\"ttl\":-1,\"type\":\"string\",\"value\":\"HHH\",\"size\":3}{\"db\":3,\"key\":\"name1\",\"ttl\":-1,\"type\":\"string\",\"value\":\"RRR\",\"size\":3}# 导出 key 包含 age 的数据{\"db\":4,\"key\":\"age\",\"ttl\":-1,\"type\":\"string\",\"value\":\"20\",\"size\":2}{\"db\":4,\"key\":\"age2\",\"ttl\":-1,\"type\":\"string\",\"value\":\"19\",\"size\":2} 导出所有数据为 JSON 文件: 1redis-dump -u 127.0.0.1:6379 > db_full.json 该命令将会在当前目录生成一个名为 db_full.json 的文件,文件内容如下: 1234567891011121314{\"db\":0,\"key\":\"name5\",\"ttl\":-1,\"type\":\"string\",\"value\":\"DDD\",\"size\":3}{\"db\":0,\"key\":\"name2\",\"ttl\":-1,\"type\":\"string\",\"value\":\"AAA\",\"size\":3}{\"db\":0,\"key\":\"name4\",\"ttl\":-1,\"type\":\"string\",\"value\":\"CCC\",\"size\":3}{\"db\":0,\"key\":\"name6\",\"ttl\":-1,\"type\":\"string\",\"value\":\"CCC\",\"size\":3}{\"db\":0,\"key\":\"name\",\"ttl\":-1,\"type\":\"string\",\"value\":\"TRHX\",\"size\":4}{\"db\":0,\"key\":\"name3\",\"ttl\":-1,\"type\":\"string\",\"value\":\"BBB\",\"size\":3}{\"db\":1,\"key\":\"name2\",\"ttl\":-1,\"type\":\"string\",\"value\":\"BBB\",\"size\":3}{\"db\":1,\"key\":\"name1\",\"ttl\":-1,\"type\":\"string\",\"value\":\"AAA\",\"size\":3}{\"db\":2,\"key\":\"name2\",\"ttl\":-1,\"type\":\"string\",\"value\":\"BBB\",\"size\":3}{\"db\":2,\"key\":\"name1\",\"ttl\":-1,\"type\":\"string\",\"value\":\"AAA\",\"size\":3}{\"db\":3,\"key\":\"name2\",\"ttl\":-1,\"type\":\"string\",\"value\":\"HHH\",\"size\":3}{\"db\":3,\"key\":\"name1\",\"ttl\":-1,\"type\":\"string\",\"value\":\"RRR\",\"size\":3}{\"db\":4,\"key\":\"age\",\"ttl\":-1,\"type\":\"string\",\"value\":\"20\",\"size\":2}{\"db\":4,\"key\":\"age2\",\"ttl\":-1,\"type\":\"string\",\"value\":\"19\",\"size\":2} 使用参数 -d 指定某个数据库的所有数据导出为 JSON 文件: 1redis-dump -u 127.0.0.1:6379 -d 4 > db_db4.json 该命令会将 4 号数据库的数据导出到 db_db4.json 文件: 12{\"db\":4,\"key\":\"age\",\"ttl\":-1,\"type\":\"string\",\"value\":\"20\",\"size\":2}{\"db\":4,\"key\":\"age2\",\"ttl\":-1,\"type\":\"string\",\"value\":\"19\",\"size\":2} 使用参数 -f 过滤数据,只导出特定的数据: 1redis-dump -u 127.0.0.1:6379 -f name > db_name.json 该命令会导出 key 包含 name 的数据到 db_name.json 文件: 123456789101112{\"db\":0,\"key\":\"name5\",\"ttl\":-1,\"type\":\"string\",\"value\":\"DDD\",\"size\":3}{\"db\":0,\"key\":\"name2\",\"ttl\":-1,\"type\":\"string\",\"value\":\"AAA\",\"size\":3}{\"db\":0,\"key\":\"name4\",\"ttl\":-1,\"type\":\"string\",\"value\":\"CCC\",\"size\":3}{\"db\":0,\"key\":\"name6\",\"ttl\":-1,\"type\":\"string\",\"value\":\"CCC\",\"size\":3}{\"db\":0,\"key\":\"name\",\"ttl\":-1,\"type\":\"string\",\"value\":\"TRHX\",\"size\":4}{\"db\":0,\"key\":\"name3\",\"ttl\":-1,\"type\":\"string\",\"value\":\"BBB\",\"size\":3}{\"db\":1,\"key\":\"name2\",\"ttl\":-1,\"type\":\"string\",\"value\":\"BBB\",\"size\":3}{\"db\":1,\"key\":\"name1\",\"ttl\":-1,\"type\":\"string\",\"value\":\"AAA\",\"size\":3}{\"db\":2,\"key\":\"name2\",\"ttl\":-1,\"type\":\"string\",\"value\":\"BBB\",\"size\":3}{\"db\":2,\"key\":\"name1\",\"ttl\":-1,\"type\":\"string\",\"value\":\"AAA\",\"size\":3}{\"db\":3,\"key\":\"name2\",\"ttl\":-1,\"type\":\"string\",\"value\":\"HHH\",\"size\":3}{\"db\":3,\"key\":\"name1\",\"ttl\":-1,\"type\":\"string\",\"value\":\"RRR\",\"size\":3} 【16.9.2】导入数据 redis-load在命令行输入 redis-load -h 可以查看: 123456789redis-load --help Try: redis-load [global options] COMMAND [command options] -u, --uri=S Redis URI (e.g. redis://hostname[:port]) -d, --database=S Redis database (e.g. -d 15) -s, --sleep=S Sleep for S seconds after dumping (for debugging) -n, --no_check_utf8 -V, --version Display version -D, --debug --nosafe 命令解释: -u Redis 连接字符串 -d 数据库代号,默认是全部 -s 导出之后的休眠时间 -n 不检测 UTF-8 编码 -V 显示版本 -D 开启调试 导入示例: 12345# 将 test.json 文件所有内容导入到数据库< test.json redis-load -u 127.0.0.1:6379# 将 test.json 文件 db 值为 6 的数据导入到数据库 < test.json redis-load -u 127.0.0.1:6379 -d 6 另外,以下方法也能导入数据: 12345# 将 test.json 文件所有内容导入到数据库cat test.json | redis-load -u 127.0.0.1:6379# 将 test.json 文件 db 值为 6 的数据导入到数据库 cat test.json | redis-load -u 127.0.0.1:6379 -d 6 注意:cat 是 Linux 系统专有的命令,在 Windows 系统里没有 cat 这个命令,可以使用 Windows 批处理命令 type 代替 cat","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫学习","slug":"Python3-学习笔记/爬虫学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫学习/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"Redis","slug":"Redis","permalink":"https://www.itrhx.com/tags/Redis/"}]},{"title":"Python3 爬虫学习笔记 C15","slug":"A45-Python3-spider-C15","date":"2019-09-10T11:46:13.293Z","updated":"2019-09-24T12:41:02.822Z","comments":true,"path":"2019/09/10/A45-Python3-spider-C15/","link":"","permalink":"https://www.itrhx.com/2019/09/10/A45-Python3-spider-C15/","excerpt":"Python3 爬虫学习笔记第十五章 —— 【代理的基本使用】","text":"Python3 爬虫学习笔记第十五章 —— 【代理的基本使用】 【15.1】代理初识大多数网站都有反爬虫机制,如果一段时间内同一个 IP 发送的请求过多,服务器就会拒绝访问,直接禁封该 IP,此时,设置代理即可解决这个问题,网络上有许多免费代理和付费代理,比如西刺代理,全网代理 IP,快代理等,设置代理需要用到的就是代理 IP 地址和端口号,如果电脑上装有代理软件(例如:酸酸乳SSR),软件一般会在本机创建 HTTP 或 SOCKS 代理服务,直接使用此代理也可以 【15.2】urllib 库使用代理1234567891011121314from urllib.error import URLErrorfrom urllib.request import ProxyHandler, build_openerproxy = '127.0.0.1:1080'proxy_handler = ProxyHandler({ 'http': 'http://' + proxy, 'https': 'https://' + proxy})opener = build_opener(proxy_handler)try: response = opener.open('http://httpbin.org/get') print(response.read().decode('utf8'))except URLError as e: print(e.reason) http://httpbin.org/get 是一个请求测试站点,借助 ProxyHandler 设置代理,参数为字典类型,键名为协议类型,键值为代理,代理的写法:proxy = '127.0.0.1:1080',其中 127.0.0.1 为 IP 地址,1080 为端口号,这里表示本机的代理软件已经在本地 1080 端口创建了代理服务,代理前面需要加上 http 或者 https 协议,当请求的链接为 http 协议时,ProxyHandler 会自动调用 http 代理,同理,当请求的链接为 https 协议时,ProxyHandler 会自动调用 https 代理,build_opener() 方法传入 ProxyHandler 对象来创建一个 opener,调用 open() 方法传入一个 url 即可通过代理访问该链接,运行结果为一个 JSON,origin 字段为此时客户端的 IP 12345678910{ \"args\": {}, \"headers\": { \"Accept-Encoding\": \"identity\", \"Host\": \"httpbin.org\", \"User-Agent\": \"Python-urllib/3.6\" }, \"origin\": \"168.70.60.141, 168.70.60.141\", \"url\": \"https://httpbin.org/get\"} 如果是需要认证的代理,只需要在代理前面加入代理认证的用户名密码即可: 1234567891011121314from urllib.error import URLErrorfrom urllib.request import ProxyHandler, build_openerproxy = 'username:password@127.0.0.1:1080'proxy_handler = ProxyHandler({ 'http': 'http://' + proxy, 'https': 'https://' + proxy})opener = build_opener(proxy_handler)try: response = opener.open('http://httpbin.org/get') print(response.read().decode('utf8'))except URLError as e: print(e.reason) 如果代理是 SOCKS5 类型,需要用到 socks 模块,设置代理方法如下: 扩展:SOCKS5 是一个代理协议,它在使用TCP/IP协议通讯的前端机器和服务器机器之间扮演一个中介角色,使得内部网中的前端机器变得能够访问 Internet 网中的服务器,或者使通讯更加安全 123456789101112import socksimport socketfrom urllib import requestfrom urllib.error import URLErrorsocks.set_default_proxy(socks.SOCKS5, '127.0.0.1', 1080)socket.socket = socks.socksockettry: response = request.urlopen('http://httpbin.org/get') print(response.read().decode('utf-8'))except URLError as e: print(e.reason) 【15.3】requests 库使用代理requests 库使用代理只需要传入 proxies 参数即可: 123456789101112import requestsproxy = '127.0.0.1:1080'proxies = ({ 'http': 'http://' + proxy, 'https': 'https://' + proxy})try: response = requests.get('http://httpbin.org/get', proxies=proxies) print(response.text)except requests.exceptions.ChunkedEncodingError as e: print('Error', e.args) 输出结果: 1234567891011{ \"args\": {}, \"headers\": { \"Accept\": \"*/*\", \"Accept-Encoding\": \"gzip, deflate\", \"Host\": \"httpbin.org\", \"User-Agent\": \"python-requests/2.22.0\" }, \"origin\": \"168.70.60.141, 168.70.60.141\", \"url\": \"https://httpbin.org/get\"} 同样的,如果是需要认证的代理,也只需要在代理前面加入代理认证的用户名密码即可: 123456789101112import requestsproxy = 'username:password@127.0.0.1:1080'proxies = ({ 'http': 'http://' + proxy, 'https': 'https://' + proxy})try: response = requests.get('http://httpbin.org/get', proxies=proxies) print(response.text)except requests.exceptions.ChunkedEncodingError as e: print('Error', e.args) 如果代理是 SOCKS5 类型,需要用到 requests[socks] 模块或者 socks 模块,使用 requests[socks] 模块时设置代理方法如下: 123456789101112import requestsproxy = '127.0.0.1:1080'proxies = { 'http': 'socks5://' + proxy, 'https': 'socks5://' + proxy}try: response = requests.get('http://httpbin.org/get', proxies=proxies) print(response.text)except requests.exceptions.ConnectionError as e: print('Error', e.args) 使用 socks 模块时设置代理方法如下(此类方法为全局设置): 1234567891011import requestsimport socksimport socketsocks.set_default_proxy(socks.SOCKS5, '127.0.0.1', 1080)socket.socket = socks.socksockettry: response = requests.get('http://httpbin.org/get') print(response.text)except requests.exceptions.ConnectionError as e: print('Error', e.args) 【15.4】Selenium 使用代理【15.4.1】Chrome12345678from selenium import webdriverproxy = '127.0.0.1:1080'chrome_options = webdriver.ChromeOptions()chrome_options.add_argument('--proxy-server=http://' + proxy)path = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe'browser = webdriver.Chrome(executable_path=path, chrome_options=chrome_options)browser.get('http://httpbin.org/get') 通过 ChromeOptions 来设置代理,在创建 Chrome 对象的时候用 chrome_options 参数传递即可,访问目标链接后显示如下信息: 12345678910111213{ \"args\": {}, \"headers\": { \"Accept\": \"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3\", \"Accept-Encoding\": \"gzip, deflate\", \"Accept-Language\": \"zh-CN,zh;q=0.9\", \"Host\": \"httpbin.org\", \"Upgrade-Insecure-Requests\": \"1\", \"User-Agent\": \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36\" }, \"origin\": \"168.70.60.141, 168.70.60.141\", \"url\": \"https://httpbin.org/get\"} 如果是认证代理,则设置方法如下: 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253from selenium import webdriverfrom selenium.webdriver.chrome.options import Optionsimport zipfileip = '127.0.0.1'port = 1080username = 'username'password = 'password'manifest_json = \"\"\"{\"version\":\"1.0.0\",\"manifest_version\": 2,\"name\":\"Chrome Proxy\",\"permissions\": [\"proxy\",\"tabs\",\"unlimitedStorage\",\"storage\",\"<all_urls>\",\"webRequest\",\"webRequestBlocking\"],\"background\": {\"scripts\": [\"background.js\"] }}\"\"\"background_js =\"\"\"var config = { mode: \"fixed_servers\", rules: { singleProxy: { scheme: \"http\", host: \"%(ip) s\", port: %(port) s } } }chrome.proxy.settings.set({value: config, scope: \"regular\"}, function() {});function callbackFn(details) { return { authCredentials: {username: \"%(username) s\", password: \"%(password) s\" } }}chrome.webRequest.onAuthRequired.addListener( callbackFn, {urls: [\"<all_urls>\"]}, ['blocking'])\"\"\" % {'ip': ip, 'port': port, 'username': username, 'password': password}plugin_file = 'proxy_auth_plugin.zip'with zipfile.ZipFile(plugin_file, 'w') as zp: zp.writestr(\"manifest.json\", manifest_json) zp.writestr(\"background.js\", background_js)chrome_options = Options()chrome_options.add_argument(\"--start-maximized\")path = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe'chrome_options.add_extension(plugin_file)browser = webdriver.Chrome(executable_path=path, chrome_options=chrome_options)browser.get('http://httpbin.org/get') 需要在本地创建一个 manifest.json 配置文件和 background.js 脚本来设置认证代理。运行代码之后本地会生成一个 proxy_auth_plugin.zip 文件来保存当前配置 【15.4.1】PhantomJS借助 service_args 参数,也就是命令行参数即可设置代理: 12345678910from selenium import webdriverservice_args = [ '--proxy=127.0.0.1:1080', '--proxy-type=http']path = r'F:\\PycharmProjects\\Python3爬虫\\phantomjs-2.1.1\\bin\\phantomjs.exe'browser = webdriver.PhantomJS(executable_path=path, service_args=service_args)browser.get('http://httpbin.org/get')print(browser.page_source) 运行结果: 12345678910111213<html><head></head><body><pre style=\"word-wrap: break-word; white-space: pre-wrap;\">{ \"args\": {}, \"headers\": { \"Accept\": \"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\", \"Accept-Encoding\": \"gzip, deflate\", \"Accept-Language\": \"zh-CN,en,*\", \"Host\": \"httpbin.org\", \"User-Agent\": \"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/538.1 (KHTML, like Gecko) PhantomJS/2.1.1 Safari/538.1\" }, \"origin\": \"168.70.60.141, 168.70.60.141\", \"url\": \"https://httpbin.org/get\"}</pre></body></html> 如果是需要认证的代理,只需要在 service_args 参数加入 –proxy-auth 选项即可: 1234567891011from selenium import webdriverservice_args = [ '--proxy=127.0.0.1:1080', '--proxy-type=http', '--proxy-auth=username:password']path = r'F:\\PycharmProjects\\Python3爬虫\\phantomjs-2.1.1\\bin\\phantomjs.exe'browser = webdriver.PhantomJS(executable_path=path, service_args=service_args)browser.get('http://httpbin.org/get')print(browser.page_source)","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫学习","slug":"Python3-学习笔记/爬虫学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫学习/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"代理","slug":"代理","permalink":"https://www.itrhx.com/tags/代理/"}]},{"title":"Python3 爬虫学习笔记 C14","slug":"A44-Python3-spider-C14","date":"2019-09-07T17:38:41.491Z","updated":"2019-09-24T12:43:31.445Z","comments":true,"path":"2019/09/08/A44-Python3-spider-C14/","link":"","permalink":"https://www.itrhx.com/2019/09/08/A44-Python3-spider-C14/","excerpt":"Python3 爬虫学习笔记第十四章 —— 【验证码对抗系列 — 点触验证码】","text":"Python3 爬虫学习笔记第十四章 —— 【验证码对抗系列 — 点触验证码】 【14.1】关于点触验证码点触验证码是由杭州微触科技有限公司研发的新一代的互联网验证码,使用点击的形式完成验证,采用专利的印刷算法以及加密算法,保证每次请求到的验证图具有极高的安全性,常见的点触验证码如下: 【14.2】点触验证码攻克思路点触验证码相对其他类型验证码比较复杂,如果依靠 OCR 图像识别点触验证码,则识别难度非常大,此时就要用到互联网的验证码服务平台,这些服务平台全部都是人工在线识别,准确率非常高,原理就是先将验证码图片提交给平台,平台会返回识别结果在图片中的坐标位置,然后我们再解析坐标模拟点击即可,常见的打码平台有超级鹰、云打码等,打码平台是收费的,拿超级鹰来说,1元 = 1000题分,识别一次验证码将花费一定的题分,不同类型验证码需要的题分不同,验证码越复杂所需题分越高,比如 7 位中文汉字需要 70 题分,常见 4 ~ 6 位英文数字只要 10 题分,其他打码平台价格也都差不多 以下以超级鹰打码平台和中国铁路12306官网来做练习 【14.3】模拟登录 12306 — 总体思路首先在超级鹰打码平台注册账号并申请一个软件 ID,官网:http://www.chaojiying.com/ ,先充值一块钱得到 1000 题分,观察 12306 官网,发现验证码是要我们点击所有满足条件的图片,一般有 1~4 张图片满足要求,由此可确定在超级鹰打码平台的验证码类型为 9004(坐标多选,返回1~4个坐标,如:x1,y1|x2,y2|x3,y3), 获取其 Python API:http://www.chaojiying.com/download/Chaojiying_Python.rar ,然后用 Selenium 模拟登陆,获取到验证码,并将验证码发送给超级鹰后台,返回识别图片的坐标,最后模拟点击即可,整个过程的实现由主程序 12306.py 和超级鹰 API chaojiying.py 组成 整个程序包含的函数: 12345678910def __init__(): 初始化 WebDriver、Chaojiying 对象等def crack(): 破解入口、获取、识别验证码、模拟登录def open(): 账号密码输入def get_screenshot(): 整个页面截图def get_touclick_element(): 获取验证码位置def get_position(): 获取验证码坐标def get_touclick_image(): 剪裁验证码部分def get_points(self, captcha_result): 分析超级鹰返回的坐标def touch_click_words(self, locations): 模拟点击符合要求的图片def login(self): 点击登陆按钮,完成模拟登录 整个程序用到的库: 1234567891011import timefrom io import BytesIOfrom PIL import Imagefrom selenium import webdriverfrom selenium.webdriver.chrome.options import Optionsfrom selenium.webdriver import ActionChainsfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECfrom chaojiying import Chaojiyingfrom selenium.common.exceptions import TimeoutException 【14.4】主函数123if __name__ == '__main__': crack = CrackTouClick() crack.crack() 【14.5】初始化函数1234567891011121314151617181920USERNAME = '155********'PASSWORD = '***********'CHAOJIYING_USERNAME = '*******'CHAOJIYING_PASSWORD = '*******'CHAOJIYING_SOFT_ID = '********'CHAOJIYING_KIND = '9004'class CrackTouClick(): def __init__(self): self.url = 'https://kyfw.12306.cn/otn/resources/login.html' path = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe' chrome_options = Options() chrome_options.add_argument('--start-maximized') self.browser = webdriver.Chrome(executable_path=path, chrome_options=chrome_options) self.wait = WebDriverWait(self.browser, 20) self.email = USERNAME self.password = PASSWORD self.chaojiying = Chaojiying_Client(CHAOJIYING_USERNAME, CHAOJIYING_PASSWORD, CHAOJIYING_SOFT_ID) 定义 12306 账号(USERNAME)、密码(PASSWORD)、超级鹰用户名(CHAOJIYING_USERNAME)、超级鹰登录密码(CHAOJIYING_PASSWORD)、超级鹰软件 ID(CHAOJIYING_SOFT_ID)、验证码类型(CHAOJIYING_KIND),登录链接 url:https://kyfw.12306.cn/otn/resources/login.html ,谷歌浏览器驱动的目录(path),浏览器启动参数,并将相关参数传递给超级鹰 API 【14.6】破解入口函数123456789101112131415161718def crack(self): self.open() image = self.get_touclick_image() bytes_array = BytesIO() image.save(bytes_array, format='PNG') result = self.chaojiying.PostPic(bytes_array.getvalue(), CHAOJIYING_KIND) print(result) locations = self.get_points(result) self.touch_click_words(locations) self.login() try: success = self.wait.until(EC.text_to_be_present_in_element((By.CSS_SELECTOR, '.welcome-name'), '用户姓名')) print(success) cc = self.browser.find_element(By.CSS_SELECTOR, '.welcome-name') print(cc.text) except TimeoutException: self.chaojiying.ReportError(result['pic_id']) self.crack() 调用 open() 函数输入账号密码 调用 get_touclick_image() 函数获取验证码图片 利用超级鹰 Python API PostPic() 方法即可把图片发送给超级鹰后台,发送的图像是字节流格式,返回的结果是一个 JSON,如果识别成功,典型的返回结果类似于:{'err_no': 0, 'err_str': 'OK', 'pic_id': '6002001380949200001', 'pic_str': '132,127|56,77', 'md5': '1f8e1d4bef8b11484cb1f1f34299865b'},其中,pic_str 就是识别的文字的坐标,是以字符串形式返回的,每个坐标都以 | 分隔 调用 get_points() 函数解析超级鹰识别结果 调用 touch_click_words() 函数对符合要求的图片进行点击,然后点击登陆按钮模拟登陆 使用 try-except 语句判断是否出现了用户信息,判断依据是是否有用户姓名的出现,出现的姓名和实际姓名一致则登录成功,如果失败了就重试,超级鹰会返回该分值 【14.7】账号密码输入函数123456789def open(self): self.browser.get(self.url) login = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.login-hd-account'))) login.click() time.sleep(3) username = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'input#J-userName'))) password = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'input#J-password'))) username.send_keys(self.email) password.send_keys(self.password) 分析页面可知,登陆页面 URL 为:https://kyfw.12306.cn/otn/resources/login.html ,该页面默认出现的是扫描二维码登陆,所以要先点击账号登录,找到该 CSS 元素为 login-hd-account,调用 click() 方法实现模拟点击,此时出现账号密码输入框,同样找到其 ID 分别为 J-userName 和 J-password,调用 send_keys() 方法输入账号密码 【14.8】页面截图函数1234def get_screenshot(self): screenshot = self.browser.get_screenshot_as_png() screenshot = Image.open(BytesIO(screenshot)) return screenshot 对整个页面进行截图 【14.9】验证码元素查找函数123def get_touclick_element(self): element = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.login-pwd-code'))) return element 同样分析页面,验证码所在位置的 CSS 为 login-pwd-code 【14.10】获取验证码坐标函数1234567def get_position(self): element = self.get_touclick_element() time.sleep(3) location = element.location size = element.size top, bottom, left, right = location['y'], location['y'] + size['height'], location['x'], location['x'] + size['width'] return (top, bottom, left, right) location 属性可以返回该图片对象在浏览器中的位置,坐标轴是以屏幕左上角为原点,x 轴向右递增,y 轴向下递增,size 属性可以返回该图片对象的高度和宽度,由此可以得到验证码的位置信息 【14.11】验证码剪裁函数123456def get_touclick_image(self, name='12306.png'): top, bottom, left, right = self.get_position() screenshot = self.get_screenshot() captcha = screenshot.crop((left, top, right, bottom)) captcha.save(name) return captcha 根据验证码的坐标信息,对页面截图进行剪裁,得到验证码部分,将其保存为 12306.png 【14.12】验证码坐标解析函数1234def get_points(self, captcha_result): groups = captcha_result.get('pic_str').split('|') locations = [[int(number) for number in group.split(',')] for group in groups] return locations get_points() 方法将超级鹰的验证码识别结果变成列表的形式 【14.13】验证码模拟点击函数1234def touch_click_words(self, locations): for location in locations: print(location) ActionChains(self.browser).move_to_element_with_offset(self.get_touclick_element(), location[0]/1.25, location[1]/1.25).click().perform() touch_click_words() 方法通过调用 move_to_element_with_offset() 方法依次传入解析后的坐标,点击即可 【14.14】模拟点击登陆函数123def login(self): submit = self.wait.until(EC.element_to_be_clickable((By.ID, 'J-login'))) submit.click() 分析页面,找到登陆按钮的 ID 为 J-login,调用 click() 方法模拟点击按钮实现登录 【14.15】效果实现动图最终实现效果图:(关键信息已经过打码处理) 【14.16】完整代码12306.py 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105import timefrom io import BytesIOfrom PIL import Imagefrom selenium import webdriverfrom selenium.webdriver.chrome.options import Optionsfrom selenium.webdriver import ActionChainsfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECfrom chaojiying import Chaojiying_Clientfrom selenium.common.exceptions import TimeoutExceptionUSERNAME = '155********'PASSWORD = '***********'CHAOJIYING_USERNAME = '***********'CHAOJIYING_PASSWORD = '***********'CHAOJIYING_SOFT_ID = '******'CHAOJIYING_KIND = '9004'class CrackTouClick(): def __init__(self): #登陆 self.url = 'https://kyfw.12306.cn/otn/resources/login.html' path = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe' chrome_options = Options() chrome_options.add_argument('--start-maximized') self.browser = webdriver.Chrome(executable_path=path, chrome_options=chrome_options) self.wait = WebDriverWait(self.browser, 20) self.email = USERNAME self.password = PASSWORD self.chaojiying = Chaojiying_Client(CHAOJIYING_USERNAME, CHAOJIYING_PASSWORD, CHAOJIYING_SOFT_ID) def crack(self): self.open() image = self.get_touclick_image() bytes_array = BytesIO() image.save(bytes_array, format='PNG') result = self.chaojiying.PostPic(bytes_array.getvalue(), CHAOJIYING_KIND) print(result) locations = self.get_points(result) self.touch_click_words(locations) self.login() try: success = self.wait.until(EC.text_to_be_present_in_element((By.CSS_SELECTOR, '.welcome-name'), '谭仁侯')) print(success) cc = self.browser.find_element(By.CSS_SELECTOR, '.welcome-name') print(cc.text) except TimeoutException: self.chaojiying.ReportError(result['pic_id']) self.crack() def open(self): self.browser.get(self.url) login = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.login-hd-account'))) login.click() time.sleep(3) username = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'input#J-userName'))) password = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'input#J-password'))) username.send_keys(self.email) password.send_keys(self.password) def get_screenshot(self): screenshot = self.browser.get_screenshot_as_png() screenshot = Image.open(BytesIO(screenshot)) return screenshot def get_touclick_element(self): element = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.login-pwd-code'))) return element def get_position(self): element = self.get_touclick_element() time.sleep(3) location = element.location size = element.size top, bottom, left, right = location['y'], location['y'] + size['height'], location['x'], location['x'] + size['width'] return (top, bottom, left, right) def get_touclick_image(self, name='12306.png'): top, bottom, left, right = self.get_position() screenshot = self.get_screenshot() captcha = screenshot.crop((left, top, right, bottom)) captcha.save(name) return captcha def get_points(self, captcha_result): groups = captcha_result.get('pic_str').split('|') locations = [[int(number) for number in group.split(',')] for group in groups] return locations def touch_click_words(self, locations): for location in locations: print(location) ActionChains(self.browser).move_to_element_with_offset(self.get_touclick_element(), location[0]/1.25, location[1]/1.25).click().perform() def login(self): submit = self.wait.until(EC.element_to_be_clickable((By.ID, 'J-login'))) submit.click()if __name__ == '__main__': crack = CrackTouClick() crack.crack() chaojiying.py 1234567891011121314151617181920212223242526272829303132333435363738394041424344import requestsfrom hashlib import md5class Chaojiying_Client(object): def __init__(self, username, password, soft_id): self.username = username password = password.encode('utf8') self.password = md5(password).hexdigest() self.soft_id = soft_id self.base_params = { 'user': self.username, 'pass2': self.password, 'softid': self.soft_id, } self.headers = { 'Connection': 'Keep-Alive', 'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)', } def PostPic(self, im, codetype): \"\"\" im: 图片字节 codetype: 题目类型 参考 http://www.chaojiying.com/price.html \"\"\" params = { 'codetype': codetype, } params.update(self.base_params) files = {'userfile': ('ccc.jpg', im)} r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, files=files, headers=self.headers) return r.json() def ReportError(self, im_id): \"\"\" im_id:报错题目的图片ID \"\"\" params = { 'id': im_id, } params.update(self.base_params) r = requests.post('http://upload.chaojiying.net/Upload/ReportError.php', data=params, headers=self.headers) return r.json()","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫学习","slug":"Python3-学习笔记/爬虫学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫学习/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"点触验证码","slug":"点触验证码","permalink":"https://www.itrhx.com/tags/点触验证码/"}]},{"title":"Python3 爬虫学习笔记 C13","slug":"A43-Python3-spider-C13","date":"2019-09-06T19:52:14.161Z","updated":"2019-09-24T12:40:56.234Z","comments":true,"path":"2019/09/07/A43-Python3-spider-C13/","link":"","permalink":"https://www.itrhx.com/2019/09/07/A43-Python3-spider-C13/","excerpt":"Python3 爬虫学习笔记第十三章 —— 【验证码对抗系列 — 滑动验证码】","text":"Python3 爬虫学习笔记第十三章 —— 【验证码对抗系列 — 滑动验证码】 【13.1】关于滑动验证码滑动验证码属于行为式验证码,需要通过用户的操作行为来完成验证,一般是根据提示用鼠标将滑块拖动到指定的位置完成验证,此类验证码背景图片采用多种图像加密技术,且添加了很多随机效果,能有效防止OCR文字识别,另外,验证码上的文字采用了随机印刷技术,能够随机采用多种字体、多种变形的实时随机印刷,防止暴力破解;斗鱼、哔哩哔哩、淘宝等平台都使用了滑动验证码 【13.2】滑动验证码攻克思路利用自动化测试工具 Selenium 直接模拟人的行为方式来完成验证,首先要分析页面,想办法找到滑动验证码的完整图片、带有缺口的图片和需要滑动的图片,通过对比原始的图片和带滑块缺口的图片的像素,像素不同的地方就是缺口位置,计算出滑块缺口的位置,得到所需要滑动的距离,最后利用 Selenium 进行对滑块的拖拽,拖拽时要模仿人的行为,由于有个对准过程,所以是先快后慢,匀速移动、随机速度移动都不会成功 以下以哔哩哔哩为例来做模拟登录练习 【13.3】模拟登录 bilibili — 总体思路首先使用 Selenium 模拟登陆 bilibili,自动输入账号密码,查找到登陆按钮并点击,使其出现滑动验证码,此时分析页面,滑动验证组件是由3个 canvas 组成,分别代表完整图片、带有缺口的图片和需要滑动的图片,3个 canvas 元素包含 CSS display 属性,display:block 为可见,display:none 为不可见,分别获取三张图片时要将其他两张图片设置为 display:none,获取元素位置后即可对图片截图并保存,通过图片像素对比,找到缺口位置即为滑块要移动的距离,随后构造滑动轨迹,按照先加速后减速的方式移动滑块完成验证。 整个程序包含的函数: 1234567891011def init(): 初始化函数,定义全局变量def login(): 登录函数,输入账号密码并点击登录def find_element(): 验证码元素查找函数,查找三张图的元素def hide_element(): 设置元素不可见函数def show_element(): 设置元素可见函数def save_screenshot(): 验证码截图函数,截取三张图并保存def slide(): 滑动函数def is_pixel_equal(): 像素判断函数,寻找缺口位置def get_distance(): 计算滑块移动距离函数def get_track(): 构造移动轨迹函数def move_to_gap(): 模拟拖动函数 整个程序用到的库: 12345678from selenium import webdriverfrom selenium.webdriver.chrome.options import Optionsfrom selenium.webdriver.support.wait import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECfrom selenium.webdriver.common.by import Byfrom selenium.webdriver import ActionChainsimport timeimport random 【13.4】主函数12345if __name__ == '__main__': init() login() find_element() slide() 【13.5】初始化函数12345678910def init(): global url, browser, username, password, wait url = 'https://passport.bilibili.com/login' path = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe' chrome_options = Options() chrome_options.add_argument('--start-maximized') browser = webdriver.Chrome(executable_path=path, chrome_options=chrome_options) username = '155********' password = '***********' wait = WebDriverWait(browser, 20) global 关键字定义了全局变量,随后是登录页面url、谷歌浏览器驱动的目录path、实例化 Chrome 浏览器、设置浏览器分辨率最大化、用户名、密码、WebDriverWait() 方法设置等待超时 【13.6】登录函数123456789def login(): browser.get(url) user = wait.until(EC.presence_of_element_located((By.ID, 'login-username'))) passwd = wait.until(EC.presence_of_element_located((By.ID, 'login-passwd'))) user.send_keys(username) passwd.send_keys(password) login_btn = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'a.btn.btn-login'))) time.sleep(random.random() * 3) login_btn.click() 等待用户名输入框和密码输入框对应的 ID 节点加载出来,分析页面可知,用户名输入框 id="login-username",密码输入框 id="login-passwd",获取这两个节点,调用 send_keys() 方法输入用户名和密码,随后获取登录按钮,分析页面可知登录按钮 class="btn btn-login",随机产生一个数并将其扩大三倍作为暂停时间,最后调用 click() 方法实现登录按钮的点击 【13.7】验证码元素查找函数12345678910111213def find_element(): c_background = wait.until( EC.presence_of_element_located((By.CSS_SELECTOR, 'canvas.geetest_canvas_bg.geetest_absolute'))) c_slice = wait.until( EC.presence_of_element_located((By.CSS_SELECTOR, 'canvas.geetest_canvas_slice.geetest_absolute'))) c_full_bg = wait.until( EC.presence_of_element_located((By.CSS_SELECTOR, 'canvas.geetest_canvas_fullbg.geetest_fade.geetest_absolute'))) hide_element(c_slice) save_screenshot(c_background, 'back') show_element(c_slice) save_screenshot(c_slice, 'slice') show_element(c_full_bg) save_screenshot(c_full_bg, 'full') 我们要获取验证码的三张图片,分别是完整的图片、带有缺口的图片和需要滑动的图片,分析页面代码,这三张图片是由 3 个 canvas 组成,3 个 canvas 元素包含 CSS display 属性,display:block 为可见,display:none 为不可见,在分别获取三张图片时要将其他两张图片设置为 display:none,这样做才能单独提取到每张图片,定位三张图片的 class 分别为:带有缺口的图片(c_background):geetest_canvas_bg geetest_absolute、需要滑动的图片(c_slice):geetest_canvas_slice geetest_absolute、完整图片(c_full_bg):geetest_canvas_fullbg geetest_fade geetest_absolute,随后传值给 save_screenshot() 函数,进一步对验证码进行处理 【13.8】元素可见性设置函数12345678# 设置元素不可见def hide_element(element): browser.execute_script("arguments[0].style=arguments[1]", element, "display: none;")# 设置元素可见def show_element(element): browser.execute_script("arguments[0].style=arguments[1]", element, "display: block;") 【13.9】验证码截图函数1234567891011121314151617181920def save_screenshot(obj, name): try: pic_url = browser.save_screenshot('.\\\\bilibili.png') print(\"%s:截图成功!\" % pic_url) left = obj.location['x'] top = obj.location['y'] right = left + obj.size['width'] bottom = top + obj.size['height'] print('图:' + name) print('Left %s' % left) print('Top %s' % top) print('Right %s' % right) print('Bottom %s' % bottom) print('') im = Image.open('.\\\\bilibili.png') im = im.crop((left, top, right, bottom)) file_name = 'bili_' + name + '.png' im.save(file_name) except BaseException as msg: print(\"%s:截图失败!\" % msg) location 属性可以返回该图片对象在浏览器中的位置,坐标轴是以屏幕左上角为原点,x轴向右递增,y轴向下递增,size 属性可以返回该图片对象的高度和宽度,由此可以得到验证码的位置信息,首先调用 save_screenshot() 属性对整个页面截图并保存,然后向 crop() 方法传入验证码的位置信息,由位置信息再对验证码进行剪裁并保存 【13.10】滑动函数123456def slide(): distance = get_distance(Image.open('.\\\\bili_back.png'), Image.open('.\\\\bili_full.png')) print('计算偏移量为:%s Px' % distance) trace = get_trace(distance - 5) move_to_gap(trace) time.sleep(3) 向 get_distance() 函数传入完整的图片和缺口图片,计算滑块需要滑动的距离,再把距离信息传入 get_trace() 函数,构造滑块的移动轨迹,最后根据轨迹信息调用 move_to_gap() 函数移动滑块完成验证 【13.11】计算滑块移动距离函数123456def get_distance(bg_image, fullbg_image): distance = 60 for i in range(distance, fullbg_image.size[0]): for j in range(fullbg_image.size[1]): if not is_pixel_equal(fullbg_image, bg_image, i, j): return i get_distance() 方法即获取缺口位置的方法,此方法的参数是两张图片,一张为完整的图片,另一张为带缺口的图片,distance 为滑块的初始位置,遍历两张图片的每个像素,利用 is_pixel_equal() 像素判断函数判断两张图片同一位置的像素是否相同,比较两张图 RGB 的绝对值是否均小于定义的阈值 threshold,如果绝对值均在阈值之内,则代表像素点相同,继续遍历,否则代表不相同的像素点,即缺口的位置 【13.12】像素判断函数123456789def is_pixel_equal(bg_image, fullbg_image, x, y): bg_pixel = bg_image.load()[x, y] fullbg_pixel = fullbg_image.load()[x, y] threshold = 60 if (abs(bg_pixel[0] - fullbg_pixel[0] < threshold) and abs(bg_pixel[1] - fullbg_pixel[1] < threshold) and abs( bg_pixel[2] - fullbg_pixel[2] < threshold)): return True else: return False 将完整图片和缺口图片两个对象分别赋值给变量 bg_image和 fullbg_image,接下来对比图片获取缺口。我们在这里遍历图片的每个坐标点,获取两张图片对应像素点的 RGB 数据,判断像素的各个颜色之差,abs() 用于取绝对值,如果二者的 RGB 数据差距在一定范围内,那就代表两个像素相同,继续比对下一个像素点,如果差距超过一定范围,则代表像素点不同,当前位置即为缺口位置 【13.13】构造移动轨迹函数123456789101112131415def get_trace(distance): trace = [] faster_distance = distance * (4 / 5) start, v0, t = 0, 0, 0.1 while start < distance: if start < faster_distance: a = 20 else: a = -20 move = v0 * t + 1 / 2 * a * t * t v = v0 + a * t v0 = v start += move trace.append(round(move)) return trace get_trace() 方法传入的参数为移动的总距离,返回的是运动轨迹,运动轨迹用 trace 表示,它是一个列表,列表的每个元素代表每次移动多少距离,利用 Selenium 进行对滑块的拖拽时要模仿人的行为,由于有个对准过程,所以是先快后慢,匀速移动、随机速度移动都不会成功,因此要设置一个加速和减速的距离,这里设置加速距离 faster_distance 是总距离 distance 的4/5倍,滑块滑动的加速度用 a 来表示,当前速度用 v 表示,初速度用 v0 表示,位移用 move 表示,所需时间用 t 表示,它们之间满足以下关系: 12move = v0 * t + 0.5 * a * t * t v = v0 + a * t 设置初始位置、初始速度、时间间隔分别为0, 0, 0.1,加速阶段和减速阶段的加速度分别设置为20和-20,直到运动轨迹达到总距离时,循环终止,最后得到的 trace 记录了每个时间间隔移动了多少位移,这样滑块的运动轨迹就得到了 【13.14】模拟拖动函数1234567def move_to_gap(trace): slider = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'div.geetest_slider_button'))) ActionChains(browser).click_and_hold(slider).perform() for x in trace: ActionChains(browser).move_by_offset(xoffset=x, yoffset=0).perform() time.sleep(0.5) ActionChains(browser).release().perform() 传入的参数为运动轨迹,首先查找到滑动按钮,然后调用 ActionChains 的 click_and_hold() 方法按住拖动底部滑块,perform() 方法用于执行,遍历运动轨迹获取每小段位移距离,调用 move_by_offset() 方法移动此位移,最后调用 release() 方法松开鼠标即可 【13.15】效果实现动图最终实现效果图:(关键信息已经过打码处理) 【13.16】完整代码123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138from selenium import webdriverfrom selenium.webdriver.chrome.options import Optionsfrom selenium.webdriver.support.wait import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECfrom selenium.webdriver.common.by import Byfrom selenium.webdriver import ActionChainsimport timeimport randomfrom PIL import Imagedef init(): global url, browser, username, password, wait url = 'https://passport.bilibili.com/login' path = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe' chrome_options = Options() chrome_options.add_argument('--start-maximized') browser = webdriver.Chrome(executable_path=path, chrome_options=chrome_options) username = '155********' password = '***********' wait = WebDriverWait(browser, 20)def login(): browser.get(url) user = wait.until(EC.presence_of_element_located((By.ID, 'login-username'))) passwd = wait.until(EC.presence_of_element_located((By.ID, 'login-passwd'))) user.send_keys(username) passwd.send_keys(password) login_btn = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'a.btn.btn-login'))) time.sleep(random.random() * 3) login_btn.click()def find_element(): c_background = wait.until( EC.presence_of_element_located((By.CSS_SELECTOR, 'canvas.geetest_canvas_bg.geetest_absolute'))) c_slice = wait.until( EC.presence_of_element_located((By.CSS_SELECTOR, 'canvas.geetest_canvas_slice.geetest_absolute'))) c_full_bg = wait.until( EC.presence_of_element_located((By.CSS_SELECTOR, 'canvas.geetest_canvas_fullbg.geetest_fade.geetest_absolute'))) hide_element(c_slice) save_screenshot(c_background, 'back') show_element(c_slice) save_screenshot(c_slice, 'slice') show_element(c_full_bg) save_screenshot(c_full_bg, 'full')def hide_element(element): browser.execute_script(\"arguments[0].style=arguments[1]\", element, \"display: none;\")def show_element(element): browser.execute_script(\"arguments[0].style=arguments[1]\", element, \"display: block;\")def save_screenshot(obj, name): try: pic_url = browser.save_screenshot('.\\\\bilibili.png') print(\"%s:截图成功!\" % pic_url) left = obj.location['x'] top = obj.location['y'] right = left + obj.size['width'] bottom = top + obj.size['height'] print('图:' + name) print('Left %s' % left) print('Top %s' % top) print('Right %s' % right) print('Bottom %s' % bottom) print('') im = Image.open('.\\\\bilibili.png') im = im.crop((left, top, right, bottom)) file_name = 'bili_' + name + '.png' im.save(file_name) except BaseException as msg: print(\"%s:截图失败!\" % msg)def slide(): distance = get_distance(Image.open('.\\\\bili_back.png'), Image.open('.\\\\bili_full.png')) print('计算偏移量为:%s Px' % distance) trace = get_trace(distance - 5) move_to_gap(trace) time.sleep(3)def get_distance(bg_image, fullbg_image): distance = 60 for i in range(distance, fullbg_image.size[0]): for j in range(fullbg_image.size[1]): if not is_pixel_equal(fullbg_image, bg_image, i, j): return idef is_pixel_equal(bg_image, fullbg_image, x, y): bg_pixel = bg_image.load()[x, y] fullbg_pixel = fullbg_image.load()[x, y] threshold = 60 if (abs(bg_pixel[0] - fullbg_pixel[0] < threshold) and abs(bg_pixel[1] - fullbg_pixel[1] < threshold) and abs( bg_pixel[2] - fullbg_pixel[2] < threshold)): return True else: return Falsedef get_trace(distance): trace = [] faster_distance = distance * (4 / 5) start, v0, t = 0, 0, 0.1 while start < distance: if start < faster_distance: a = 20 else: a = -20 move = v0 * t + 1 / 2 * a * t * t v = v0 + a * t v0 = v start += move trace.append(round(move)) return tracedef move_to_gap(trace): slider = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'div.geetest_slider_button'))) ActionChains(browser).click_and_hold(slider).perform() for x in trace: ActionChains(browser).move_by_offset(xoffset=x, yoffset=0).perform() time.sleep(0.5) ActionChains(browser).release().perform()if __name__ == '__main__': init() login() find_element() slide()","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫学习","slug":"Python3-学习笔记/爬虫学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫学习/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"滑动验证码","slug":"滑动验证码","permalink":"https://www.itrhx.com/tags/滑动验证码/"}]},{"title":"Python3 爬虫学习笔记 C12","slug":"A42-Python3-spider-C12","date":"2019-09-05T14:54:48.887Z","updated":"2019-09-24T12:40:54.127Z","comments":true,"path":"2019/09/05/A42-Python3-spider-C12/","link":"","permalink":"https://www.itrhx.com/2019/09/05/A42-Python3-spider-C12/","excerpt":"Python3 爬虫学习笔记第十二章 —— 【验证码对抗系列 — 图形验证码】","text":"Python3 爬虫学习笔记第十二章 —— 【验证码对抗系列 — 图形验证码】 【12.1】关于普通图形验证码普通图形验证码一般由四位纯数字、纯字母或者字母数字组合构成,是最常见的验证码,也是最简单的验证码,利用 tesserocr 或者 pytesseract 库即可识别此类验证码,前提是已经安装好 Tesseract-OCR 软件 【12.2】tesserocr 库识别验证码简单示例: 123456import tesserocrfrom PIL import Imageimage = Image.open('code.png')result = tesserocr.image_to_text(image)print(result) 新建一个 Image 对象,调用 tesserocr 的 image_to_text() 方法,传入 Image 对象即可完成识别,另一种方法: 12import tesserocrprint(tesserocr.file_to_text('code.png')) 【12.3】pytesseract 库识别验证码简单示例: 1234567import pytesseractfrom PIL import Imageimg = Image.open('code.png')img = img.convert('RGB')img.show()print(pytesseract.image_to_string(img)) pytesseract 的各种方法: get_tesseract_version:返回 Tesseract 的版本信息; image_to_string:将图像上的 Tesseract OCR 运行结果返回到字符串; image_to_boxes:返回包含已识别字符及其框边界的结果; image_to_data:返回包含框边界,置信度和其他信息的结果。需要 Tesseract 3.05+; image_to_osd:返回包含有关方向和脚本检测的信息的结果。 有关参数: image_to_data(image, lang='', config='', nice=0, output_type=Output.STRING) image:图像对象; lang:Tesseract 语言代码字符串; config:任何其他配置为字符串,例如:config=’–psm 6’; nice:修改 Tesseract 运行的处理器优先级。Windows不支持。尼斯调整了类似 unix 的流程的优点; output_type:类属性,指定输出的类型,默认为string。 lang 参数,常见语言代码如下: chi_sim:简体中文 chi_tra:繁体中文 eng:英文 rus:俄罗斯语 fra:法语 deu:德语 jpn:日语 【12.4】验证码处理利用 Image 对象的 convert() 方法传入不同参数可以对验证码做一些额外的处理,如转灰度、二值化等操作,经过处理过后的验证码会更加容易被识别,识别准确度更高,各种参数及含义: 1:1位像素,黑白,每字节一个像素存储; L:8位像素,黑白; P:8位像素,使用调色板映射到任何其他模式; RGB:3x8位像素,真彩色; RGBA:4x8位像素,带透明度掩模的真彩色; CMYK:4x8位像素,分色; YCbCr:3x8位像素,彩色视频格式; I:32位有符号整数像素; F:32位浮点像素。 示例: 12345678import pytesseractfrom PIL import Imageimage = Image.open('code.png')image = image.convert('L')image.show()result = pytesseract.image_to_string(image)print(result) Image 对象的 convert() 方法参数传入 L,即可将图片转化为灰度图像,转换前后对比: 12345678import pytesseractfrom PIL import Imageimage = Image.open('code.png')image = image.convert('1')image.show()result = pytesseract.image_to_string(image)print(result) Image 对象的 convert() 方法参数传入 1,即可将图片进行二值化处理,处理前后对比: 【12.5】tesserocr 与 pytesserocr 相关资料 tesserocr GitHub:https://github.com/sirfz/tesserocr tesserocr PyPI:https://pypi.python.org/pypi/tesserocr pytesserocr GitHub:https://github.com/madmaze/pytesseract pytesserocr PyPI:https://pypi.org/project/pytesseract/ Tesseract-OCR 下载地址:http://digi.bib.uni-mannheim.de/tesseract tesseract GitHub:https://github.com/tesseract-ocr/tesseract tesseract 语言包:https://github.com/tesseract-ocr/tessdata tesseract 文档:https://github.com/tesseract-ocr/tesseract/wiki/Documentation","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫学习","slug":"Python3-学习笔记/爬虫学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫学习/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"图形验证码","slug":"图形验证码","permalink":"https://www.itrhx.com/tags/图形验证码/"}]},{"title":"Python3 爬虫学习笔记 C11","slug":"A41-Python3-spider-C11","date":"2019-09-04T14:06:03.110Z","updated":"2019-09-24T12:40:45.074Z","comments":true,"path":"2019/09/04/A41-Python3-spider-C11/","link":"","permalink":"https://www.itrhx.com/2019/09/04/A41-Python3-spider-C11/","excerpt":"Python3 爬虫学习笔记第十一章 —— 【MongoDB数据储存】","text":"Python3 爬虫学习笔记第十一章 —— 【MongoDB数据储存】 【11.1】关于 MongoDBMongoDB 属于非关系型数据库,即 NoSQL(Not Only SQL),NoSQL 是基于键值对的,不需要经过 SQL 层的解析,数据之间没有耦合性,性能极高,非关系型数据库分为以下几种: 键值存储数据库:Redis、Voldemort、Oracle BDB 等; 列存储数据库:Cassandra、HBase、Riak 等; 文档型数据库:CouchDB、MongoDB 等; 图形数据库:Neo4J、InfoGrid、Infinite Graph 等。 【11.2】MongoDB 基本操作语句123456789101112131415161718192021222324252627282930# 创建数据库(如果数据库不存在就创建数据库, 存在就切换到指定的数据库)use DATABASE_NAME# 查看所有数据库show dbs# 查看当前所在数据库db# 删除当前数据库db.dropDatabase()# 删除集合db.COLLECTION_NAME.drop()# 创建集合db.createCollection(\"COLLECTION_NAME\")# 插入文档db.COLLECTION_NAME.insert(document)db.COLLECTION_NAME.save(document) # 更新文档db.COLLECTION_NAME.update()# 删除文档db.COLLECTION_NAME.remove()# 查询文档db.COLLECTION_NAME.find(query, projection) 【11.3】连接 MongoDB连接 MongoDB 需要导入 pymongo 库,使用 MongoClient() 方法,向其传入地址参数 host 和 端口参数 port 即可 123import pymongoclient = pymongo.MongoClient(host='localhost', port=27017) 也可以直接传入 MongoDB 的连接字符串: 123import pymongoclient = pymongo.MongoClient('mongodb://localhost:27017/') 【11.4】指定数据库使用以下语句皆可指定一个名为 spiders 的数据库: 1db = client.spiders 1db = client['spiders'] 【11.5】指定集合MongoDB 的每个数据库包含多个集合(collection),类似于关系型数据库 MySQL 中的数据表,使用以下语句皆可指定一个名为 students 的集合: 1collection = db.students 1collection = db['students'] 【11.6】插入数据12345678910111213import pymongoclient = pymongo.MongoClient(host='localhost', port=27017)db = client.spiderscollection = db.studentsstudents = { 'id': '17110105', 'name': 'TRHX', 'age': 20, 'gender': 'male'}result = collection.insert(students)print(result) 在 spiders 数据库的 students 集合里,新建一条学生数据,该数据以字典形式表示,调用 collection 的 insert() 方法插入数据,在 MongoDB 中,每条数据都有一个_id 属性来唯一标识。如果没有显式指明该属性,MongoDB 会自动产生一个 ObjectId 类型的_id 属性。insert() 方法会在执行后返回 _id 值,在 MongoDB 数据库里面可以看到已经成功插入数据,输出结果: 15d6f1a4b57b65e1547bb3c24 进阶操作:同时插入多条数据,以列表形式传递: 12345678910111213141516171819import pymongoclient = pymongo.MongoClient(host='localhost', port=27017)db = client.spiderscollection = db.studentsstudents1 = { 'id': '17110105', 'name': 'TRHX', 'age': 20, 'gender': 'male'}students2 = { 'id': '17110106', 'name': 'AAAA', 'age': 22, 'gender': 'male'}result = collection.insert([students1, students2])print(result) 输出结果: 1[ObjectId('5d6f2be3cd1721962218a709'), ObjectId('5d6f2be3cd1721962218a70a')] PyMongo 3.x 及以上版本中,推荐使用 insert_one() 和 insert_many() 方法来分别插入单条记录和多条记录,示例: 插入单条记录 1234567891011121314import pymongoclient = pymongo.MongoClient(host='localhost', port=27017)db = client.spiderscollection = db.studentsstudents = { 'id': '17110105', 'name': 'TRHX', 'age': 20, 'gender': 'male'}result = collection.insert_one(students)print(result)print(result.inserted_id) 返回的是 InsertOneResult 对象,调用其 inserted_id 属性获取_id: 12<pymongo.results.InsertOneResult object at 0x0000020ED91A5608>5d6f73940fe700c5a7ac19f0 插入多条记录 1234567891011121314151617181920import pymongoclient = pymongo.MongoClient(host='localhost', port=27017)db = client.spiderscollection = db.studentsstudents1 = { 'id': '17110105', 'name': 'TRHX', 'age': 20, 'gender': 'male'}students2 = { 'id': '17110106', 'name': 'AAAA', 'age': 22, 'gender': 'male'}result = collection.insert_many([students1, students2])print(result)print(result.inserted_ids) 返回的类型是 InsertManyResult,调用 inserted_ids 属性可以获取插入数据的_id 列表: 12<pymongo.results.InsertManyResult object at 0x0000021698DD36C8>[ObjectId('5d6f68598fa881c69b2e0006'), ObjectId('5d6f68598fa881c69b2e0007')] 【11.6】数据查询事先已经创建好 spiders 数据库和 students 集合,包含以下数据: 1234567891011121314151617181920212223_id:ObjectId(\"5d6f95d40828142f1dc35fa5\")id:\"17110105\"name:\"TRHX\"age:20gender:\"male\"_id:ObjectId(\"5d6f95d40828142f1dc35fa6\")id:\"17110106\"name:\"AAA\"age:20gender:\"male\"_id:ObjectId(\"5d6f95d40828142f1dc35fa7\")id:\"17110107\"name:\"BBB\"age:19gender:\"female\"_id:ObjectId(\"5d6f95d40828142f1dc35fa8\")id:\"17110108\"name:\"CCC\"age:22gender:\"male\" 查询方法一:利用 find_one() 或 find() 方法进行查询, find_one() 查询得到的是单个结果,find() 则返回一个生成器对象 1234567import pymongoclient = pymongo.MongoClient(host='localhost', port=27017)db = client.spiderscollection = db.studentsresult = collection.find_one({'name': 'TRHX'})print(result) 查询 name 为 TRHX 的数据,返回一个字典类型: 1{'_id': ObjectId('5d6f95d40828142f1dc35fa5'), 'id': '17110105', 'name': 'TRHX', 'age': 20, 'gender': 'male'} 查询方法二:根据 ObjectId 查询,查询时需要使用 bson 库里面的 objectid: 12345678import pymongofrom bson.objectid import ObjectIdclient = pymongo.MongoClient(host='localhost', port=27017)db = client.spiderscollection = db.studentsresult = collection.find_one({'_id': ObjectId('5d6f95d40828142f1dc35fa7')})print(result) 查询结果: 1{'_id': ObjectId('5d6f95d40828142f1dc35fa7'), 'id': '17110107', 'name': 'BBB', 'age': 19, 'gender': 'female'} 使用 find() 方法查询多条数据: 123456789import pymongoclient = pymongo.MongoClient(host='localhost', port=27017)db = client.spiderscollection = db.studentsresults = collection.find({'gender': 'male'})print(results)for result in results: print(result) find() 方法返回一个生成器对象,遍历得到所有数据,每条数据都是字典类型: 1234<pymongo.cursor.Cursor object at 0x00000191F69AAA90>{'_id': ObjectId('5d6f95d40828142f1dc35fa5'), 'id': '17110105', 'name': 'TRHX', 'age': 20, 'gender': 'male'}{'_id': ObjectId('5d6f95d40828142f1dc35fa6'), 'id': '17110106', 'name': 'AAA', 'age': 20, 'gender': 'male'}{'_id': ObjectId('5d6f95d40828142f1dc35fa8'), 'id': '17110108', 'name': 'CCC', 'age': 22, 'gender': 'male'} 在查询条件中加入比较符号进行查询,以下代码实现了年龄大于等于20的数据查询: 12345678import pymongoclient = pymongo.MongoClient(host='localhost', port=27017)db = client.spiderscollection = db.studentsresults = collection.find({'age': {'$gte': 20}})for result in results: print(result) 符号 $gte 表示大于等于,查询结果如下: 123{'_id': ObjectId('5d6f95d40828142f1dc35fa5'), 'id': '17110105', 'name': 'TRHX', 'age': 20, 'gender': 'male'}{'_id': ObjectId('5d6f95d40828142f1dc35fa6'), 'id': '17110106', 'name': 'AAA', 'age': 20, 'gender': 'male'}{'_id': ObjectId('5d6f95d40828142f1dc35fa8'), 'id': '17110108', 'name': 'CCC', 'age': 22, 'gender': 'male'} 附表:各种比较符号 符号 含义 示例 $lt 小于 {‘age’: {‘$lt’: 20}} $gt 大于 {‘age’: {‘$gt’: 20}} $lte 小于等于 {‘age’: {‘$lte’: 20}} $gte 大于等于 {‘age’: {‘$gte’: 20}} $ne 不等于 {‘age’: {‘$ne’: 20}} $in 在范围内 {‘age’: {‘$in’: [20, 23]}} $nin 不在范围内 {‘age’: {‘$nin’: [20, 23]}} 在查询条件中加入功能符号进行查询,以下代码用正则匹配实现了对名字以 T 开头的学生数据的查询: 12345678import pymongoclient = pymongo.MongoClient(host='localhost', port=27017)db = client.spiderscollection = db.studentsresults = collection.find({'name': {'$regex': '^T.*'}})for result in results: print(result) 查询结果: 1{'_id': ObjectId('5d6f95d40828142f1dc35fa5'), 'id': '17110105', 'name': 'TRHX', 'age': 20, 'gender': 'male'} 附表:各种功能符号 符号 含义 示例 示例含义 $regex 匹配正则表达式 {‘name’: {‘$regex’: ‘^T.*’}} name 以 T 开头 $exists 属性是否存在 {‘name’: {‘$exists’: True}} name 属性存在 $type 类型判断 {‘age’: {‘$type’: ‘int’}} age 的类型为 int $mod 数字模操作 {‘age’: {‘$mod’: [5, 0]}} 年龄模 5 余 0 $text 文本查询 {‘$text’: {‘$search’: ‘Mike’}} text 类型的属性中包含 Mike 字符串 $where 高级条件查询 {‘$where’: ‘obj.fans_count == obj.follows_count’} 自身粉丝数等于关注数 其他操作:https://docs.mongodb.com/manual/reference/operator/query/ 【11.7】数据计数调用 count() 方法可以统计查询结果有多少条数据,输出结果为一个整数: 1234567import pymongoclient = pymongo.MongoClient(host='localhost', port=27017)db = client.spiderscollection = db.studentsresult = collection.find({'name': {'$regex': '^T.*'}}).count()print(result) 【11.8】数据排序调用 sort() 方法,向其传入排序的字段及升降序标志即可完成排序: 123456789import pymongoclient = pymongo.MongoClient(host='localhost', port=27017)db = client.spiderscollection = db.studentsascending = collection.find().sort('name', pymongo.ASCENDING)descending = collection.find().sort('name', pymongo.DESCENDING)print('升序排列:', [result['name'] for result in ascending])print('降序排列:', [result['name'] for result in descending]) 输出结果: 12升序排列: ['AAA', 'BBB', 'CCC', 'TRHX']降序排列: ['TRHX', 'CCC', 'BBB', 'AAA'] 【11.9】数据偏移利用 skip() 方法偏移几个位置,就可以跳过前几条数据,获取偏移量之后的几个数据;利用 limit() 方法指定获取前几条数据: 123456789import pymongoclient = pymongo.MongoClient(host='localhost', port=27017)db = client.spiderscollection = db.studentsascending = collection.find().sort('name', pymongo.ASCENDING).skip(1)descending = collection.find().sort('name', pymongo.DESCENDING).limit(2)print('升序排列(偏移量为1,获取后三条数据):', [result['name'] for result in ascending])print('降序排列(限制获取前两条数据):', [result['name'] for result in descending]) 输出结果: 12升序排列(偏移量为1,获取后三条数据): ['BBB', 'CCC', 'TRHX']降序排列(限制获取前两条数据): ['TRHX', 'CCC'] 【11.10】更新数据使用 update() 方法,指定更新的条件和更新后的数据即可: 12345678910import pymongoclient = pymongo.MongoClient(host='localhost', port=27017)db = client.spiderscollection = db.studentscondition = {'name': 'TRHX'}student = collection.find_one(condition)student['age'] = 18result = collection.update(condition, student)print(result) 该代码将 name 为 TRHX 的 age 改为了 18,返回结果仍然是字典形式,ok 代表执行成功,nModified 代表影响的数据条数: 1{'n': 1, 'nModified': 1, 'ok': 1.0, 'updatedExisting': True} 进阶操作:使用 $set 操作符对数据进行更新,指定更新的条件和更新后的数据即可,这样做的好处是:只更新指定的 student 字典内存在的字段,如果原先还有其他字段,则不会更新,也不会删除;如果不用 $set ,则会把之前的数据全部用 student 字典替换,如果原本存在其他字段,则会被删除 12345678910import pymongoclient = pymongo.MongoClient(host='localhost', port=27017)db = client.spiderscollection = db.studentscondition = {'name': 'TRHX'}student = collection.find_one(condition)student['age'] = 18result = collection.update(condition, {'$set': student})print(result) 和插入数据的 insert() 方法一样,在 PyMongo 3.x 版本里,推荐使用 update_one() 和 update_many() 方法 1234567891011import pymongoclient = pymongo.MongoClient(host='localhost', port=27017)db = client.spiderscollection = db.studentscondition = {'name': 'TRHX'}student = collection.find_one(condition)student['age'] = 19result = collection.update_one(condition, {'$set': student})print(result)print(result.matched_count, result.modified_count) 注意:update_one() 方法不能直接传入修改后的字典,只能使用 {'$set': student} 的形式传入,可以调用 matched_count 和 modified_count 属性,获取匹配的数据条数和影响的数据条数: 12<pymongo.results.UpdateResult object at 0x00000235A1684508>1 1 使用update_many() 方法可以将所有符合条件的数据都更新: 123456789import pymongoclient = pymongo.MongoClient(host='localhost', port=27017)db = client.spiderscollection = db.studentscondition = {'age': {'$gt': 18}}result = collection.update_many(condition, {'$set': {'age': 25}})print(result)print(result.matched_count, result.modified_count) 匹配所有年龄大于 18 的数据,更新条件为将这些所有满足条件的年龄都设置成 25,输出结果如下: 12<pymongo.results.UpdateResult object at 0x00000285CECC45C8>4 4 【11.11】删除数据调用 remove() 方法并指定删除的条件,此时符合条件的所有数据均会被删除 1234567import pymongoclient = pymongo.MongoClient(host='localhost', port=27017)db = client.spiderscollection = db.studentsresult = collection.remove({'name': 'CCC'})print(result) 输出结果: 1{'n': 1, 'ok': 1.0} 同样的,在 PyMongo 3.x 版本里,推荐使用 delete_one() 和 delete_many() 方法 12345678910import pymongoclient = pymongo.MongoClient(host='localhost', port=27017)db = client.spiderscollection = db.studentsresult = collection.delete_one({'name': 'AAA'})print(result)print(result.deleted_count)result = collection.delete_many({'gender': 'female'})print(result.deleted_count) 调用 deleted_count 属性可以获取删除的数据条数,输出结果: 123<pymongo.results.DeleteResult object at 0x0000024441B245C8>11 PyMongo 官方文档:http://api.mongodb.com/python/current/api/pymongo/collection.html","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫学习","slug":"Python3-学习笔记/爬虫学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫学习/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"MongoDB","slug":"MongoDB","permalink":"https://www.itrhx.com/tags/MongoDB/"}]},{"title":"Python3 爬虫学习笔记 C10","slug":"A40-Python3-spider-C10","date":"2019-09-03T15:39:27.326Z","updated":"2020-03-14T06:21:36.668Z","comments":true,"path":"2019/09/03/A40-Python3-spider-C10/","link":"","permalink":"https://www.itrhx.com/2019/09/03/A40-Python3-spider-C10/","excerpt":"Python3 爬虫学习笔记第十章 —— 【MySQL数据储存】","text":"Python3 爬虫学习笔记第十章 —— 【MySQL数据储存】 【10.1】MySQL 基本操作语句安装完 MySQL 后,打开 MySQL x.x Command Line Client - Unicode,输入密码即可登录 MySQL,也可在 MySQL 安装目录下打开 cmd 使用命令登录数据库 数据库操作1234567891011121314151617# 连接数据库mysql -u root -p# 退出数据库exit# 查看所有的数据库SHOW DATABASES;# 创建一个数据库CREATE DATABASE X;# 删除一个数据库DROP DATABASE IF EXISTS X;# 使用这个数据库USE X; 表操作12345678910111213141516171819202122232425262728293031323334353637# 查看所有的表SHOW TABLES ;# 创建一个表CREATE TABLE n(id INT, name VARCHAR(10));CREATE TABLE m(id INT, name VARCHAR(10), PRIMARY KEY (id), FOREIGN KEY (id) REFERENCES n(id), UNIQUE (name));CREATE TABLE m(id INT, name VARCHAR(10));# 直接将查询结果导入或复制到新创建的表CREATE TABLE n SELECT * FROM m;# 新创建的表与一个存在的表的数据结构类似CREATE TABLE m LIKE n;# 创建一个临时表# 临时表将在你连接MySQL期间存在。当断开连接时,MySQL将自动删除表并释放所用的空间。也可手动删除。CREATE TEMPORARY TABLE l(id INT, name VARCHAR(10));# 直接将查询结果导入或复制到新创建的临时表CREATE TEMPORARY TABLE tt SELECT * FROM n;# 删除一个存在表DROP TABLE IF EXISTS m;# 更改存在表的名称ALTER TABLE n RENAME m;RENAME TABLE n TO m;# 查看表的结构(以下五条语句效果相同)DESC n;DESCRIBE n;SHOW COLUMNS IN n;SHOW COLUMNS FROM n;EXPLAIN n;# 查看表的创建语句SHOW CREATE TABLE n; 表的结构12345678910111213141516171819202122232425# 添加字段ALTER TABLE n ADD age VARCHAR(2);# 添加字段时设定位置ALTER TABLE n ADD age VARCHAR(2) FIRST;ALTER TABLE n ADD age VARCHAR(2) AFTER name;# 修改字段在表中的位置ALTER TABLE n MODIFY age VARCHAR(2) AFTER name;# 删除字段ALTER TABLE n DROP age;# 更改字段属性和属性ALTER TABLE n CHANGE age a INT;# 只更改字段属性ALTER TABLE n MODIFY age VARCHAR(7) ;# 改变表的存储引擎ALTER TABLE t ENGINE myisam;ALTER TABLE t ENGINE innodb;# 设定自增 初始为1,只能一个字段使用,该字段为主键的一部分ALTER TABLE t AUTO_INCREMENT = 0; 表的数据123456789101112131415# 增加数据INSERT INTO n VALUES (1, 'tom', '23'), (2, 'john', '22');INSERT INTO n SELECT * FROM n; # 把数据复制一遍重新插入# 删除数据DELETE FROM n WHERE id = 2;# 更改数据UPDATE n SET name = 'tom' WHERE id = 2;# 数据查找SELECT * FROM n WHERE name LIKE '%h%';# 数据排序(反序)SELECT * FROM n ORDER BY name, id DESC ; 【10.2】Python 连接 MySQL123456789import pymysqldb = pymysql.connect(host='localhost', user='root', password='000000', port=3306)cursor = db.cursor()cursor.execute('SELECT VERSION()')data = cursor.fetchone()print('Database version:', data)cursor.execute(\"CREATE DATABASE spiders DEFAULT CHARACTER SET utf8mb4\")db.close() 通过 PyMySQL 的 connect 方法声明一个 MySQL 连接对象 db,当前 MySQL 数据库运行在本地,设定 host='localhost',用户名为 root,登录密码为 000000,运行在 3306 端口,调用 cursor() 方法获得 MySQL 的操作游标,该游标用来执行 SQL 语句,通过游标操作 execute() 方法写入 SQL 语句,第一条 SQL 语句获取 MySQL 的版本信息,调用 fetchone() 方法获得第一条数据,即 MySQL 的版本号。第二条 SQL 语句执行创建 spiders 数据库的操作,编码为 utf8mb4,运行该段代码将输出 MySQL 的版本号: 1Database version: ('8.0.17',) 【10.3】创建表1234567import pymysqldb = pymysql.connect(host='localhost', user='root', password='000000', port=3306, db='spiders')cursor = db.cursor()sql = 'CREATE TABLE IF NOT EXISTS students (id VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, age VARCHAR(255) NOT NULL, PRIMARY KEY (id))'cursor.execute(sql)db.close() 该段代码实现了在 spiders 数据库里创建了一个名为 students 的表,包含 id、name、age 三个字段,类型依次为 varchar、varchar、int 【10.4】插入数据1234567891011121314import pymysqlid = '17110105'user = 'TRH'age = 20db = pymysql.connect(host='localhost', user='root', password='000000', port=3306, db='spiders')cursor = db.cursor()sql = 'INSERT INTO students(id, name, age) values(%s, %s, %s)'try: cursor.execute(sql, (id, user, age)) db.commit()except: db.rollback()db.close() commit() 方法的作用是实现数据插入,是真正将语句提交到数据库执行的方法,使用 try except 语句实现异常处理,如果执行失败,则调用 rollback() 方法执行数据回滚,保证原数据不被破坏,使用查询语句可以看到已经插入的数据: 进阶操作:将需要插入的数据构造成一个字典,这样的做法可以让插入方法无需改动,只需要传入一个动态变化的字典就行了,改写原来的代码如下: 123456789101112131415161718192021import pymysqldata = { 'id': '17110105', 'name': 'TRH', 'age': 20}table = 'students'keys = ', '.join(data.keys())values = ', '.join(['%s']*len(data))db = pymysql.connect(host='localhost', user='root', password='000000', port=3306, db='spiders')cursor = db.cursor()sql = 'INSERT INTO {table}({keys}) VALUES ({values})'.format(table=table, keys=keys, values=values)try: cursor.execute(sql, tuple(data.values())) print('数据插入成功!') db.commit()except: print('数据插入失败!') db.rollback()db.close() 传入的数是字典,将其定义为 data 变量,表名定义成变量 table,构造插入的字段 id、name 和 age。', '.join(data.keys()) 的结果就是 id, name, age,接着需要构造多个 %s 当作占位符,有三个字段,就需要构造 %s, %s, %s。首先定义长度为 1 的数组 ['%s'],然后用乘法将其扩充为 ['%s', '%s', '%s'],再调用 join() 方法,最终变成 %s, %s, %s。再利用字符串的 format() 方法将表名、字段名和占位符构造出来。最终的 SQL 语句就被动态构造成了如下语句: 1INSERT INTO students(id, name, age) VALUES (%s, %s, %s) 【10.5】更新数据1234567891011121314151617181920212223import pymysqldata = { 'id': '17110105', 'name': 'TRH', 'age': 21}table = 'students'keys = ', '.join(data.keys())values = ', '.join(['%s']*len(data))db = pymysql.connect(host='localhost', user='root', password='000000', port=3306, db='spiders')cursor = db.cursor()sql = 'INSERT INTO {table}({keys}) VALUES ({values}) ON DUPLICATE KEY UPDATE'.format(table=table, keys=keys, values=values)update = ','.join([\"{key} = % s\".format(key=key) for key in data])sql += updatetry: if cursor.execute(sql, tuple(data.values())*2): print('数据插入成功!') db.commit()except: print('数据插入失败!') db.rollback()db.close() ON DUPLICATE KEY UPDATE 表示如果主键已经存在,就执行更新操作,最终被构造成如下语句: 1INSERT INTO students(id, name, age) VALUES (% s, % s, % s) ON DUPLICATE KEY UPDATE id = % s, name = % s, age = % s 【10.6】删除数据123456789101112131415import pymysqltable = 'students'condition = 'age = 20'db = pymysql.connect(host='localhost', user='root', password='000000', port=3306, db='spiders')cursor = db.cursor()sql = 'DELETE FROM {table} WHERE {condition}'.format(table=table, condition=condition)try: cursor.execute(sql) print('数据删除成功!') db.commit()except: print('数据删除失败!') db.rollback()db.close() 删除操作直接使用 DELETE 语句,指定要删除的目标表名和删除条件即可 【10.7】查询数据123456789101112131415161718import pymysqltable = 'students'db = pymysql.connect(host='localhost', user='root', password='000000', port=3306, db='spiders')cursor = db.cursor()sql = 'SELECT * FROM students WHERE age >= 20'try: cursor.execute(sql) print('Count:', cursor.rowcount) one = cursor.fetchone() print('One:', one) results = cursor.fetchall() print('Results:', results) print('Results Type:', type(results)) for row in results: print(row)except: print('查询失败!') sql = 'SELECT * FROM students WHERE age >= 20':构造一条 SQL 语句,将年龄 大于等于20 岁的学生查询出来 cursor.rowcount:调用 cursor 的 rowcount 属性获取查询结果的条数 cursor.fetchone():调用 cursor 的 fetchone() 方法,获取结果的第一条数据,返回结果是元组形式,元组的元素顺序跟字段一一对应,即第一个元素就是第一个字段 id,第二个元素就是第二个字段 name,以此类推 cursor.fetchall():调用 cursor 的 fetchall() 方法,得到结果的所有数据,它是二重元组,每个元素都是一条记录,本例中显示的是 3 条数据而不是 4 条,这是因为它的内部实现有一个偏移指针用来指向查询结果,最开始偏移指针指向第一条数据,取一次之后,指针偏移到下一条数据,这样再取的话,就会取到下一条数据了。我们最初调用了一次 fetchone 方法,这样结果的偏移指针就指向下一条数据,fetchall 方法返回的是偏移指针指向的数据一直到结束的所有数据,所以该方法获取的结果就只剩 3 个了 【10.8】实战训练 — 爬取CSDN博客标题和地址保存到 MySQL利用 requests 库构建请求,BeautifulSoup 解析库解析网页,获取自己博客文章的标题和地址,将其储存到本地 MySQL 数据库中,事先已经创建好了一个 blog 数据库,并创建了一个名为 article 的数据表,数据表包含 id、title、url 三个字段,其中 id 的 AUTO_INCREMENT 属性可以使 id 自己增加,PRIMARY KEY 关键字用于将 id 定义为主键 创建 article 数据表: 1234567import pymysqldb = pymysql.connect(host='localhost', user='root', password='000000', port=3306, db='blog')cursor = db.cursor()sql = 'CREATE TABLE IF NOT EXISTS article (id INT NOT NULL AUTO_INCREMENT, title VARCHAR(255) NOT NULL, url VARCHAR(255) NOT NULL, PRIMARY KEY (id))'cursor.execute(sql)db.close() 获取文章标题和对应的 URL 并将其储存到 MySQL 中: 123456789101112131415161718192021222324import requestsimport pymysqlfrom bs4 import BeautifulSoupdb = pymysql.connect(host='localhost', user='root', password='000000', port=3306, db='blog')cursor = db.cursor()headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36',}url = \"https://blog.csdn.net/qq_36759224\"request = requests.get(url, headers=headers)soup = BeautifulSoup(request.text, 'lxml')title_list = soup.find_all('h4')for list in title_list: s = list.a.text.strip() title = s.replace('原', '') url = list.a['href'].strip() # print(title + url) cursor.execute('INSERT INTO article (title, url) VALUES (%s, %s)', (title, url))db.commit()print('数据写入完毕!')db.close() 在命令行中使用 SELECT * FROM article; 命令可以查看到数据已经成功获取并储存到了数据库中:","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫学习","slug":"Python3-学习笔记/爬虫学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫学习/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"MySQL","slug":"MySQL","permalink":"https://www.itrhx.com/tags/MySQL/"}]},{"title":"Python3 爬虫学习笔记 C09","slug":"A39-Python3-spider-C09","date":"2019-08-27T10:58:37.295Z","updated":"2019-10-09T10:40:08.565Z","comments":true,"path":"2019/08/27/A39-Python3-spider-C09/","link":"","permalink":"https://www.itrhx.com/2019/08/27/A39-Python3-spider-C09/","excerpt":"Python3 爬虫学习笔记第九章 —— 【文件储存】","text":"Python3 爬虫学习笔记第九章 —— 【文件储存】 用解析器解析出数据之后,还需要对数据进行保存。保存的形式多种多样,最简单的形式是直接保存为文本文件,如 TXT、JSON、CSV 等。 【9.1】TXT 文本存储TXT 文本存储的优点:操作非常简单,TXT 文本几乎兼容任何平台;缺点:不利于检索。 【9.1.1】基本示例1234567891011121314import requestsfrom lxml import etreeheaders = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36',}url = \"https://blog.csdn.net/qq_36759224\"request = requests.get(url, headers=headers)tree = etree.HTML(request.text)title_list = tree.xpath('//h4/a/text()')for title in title_list: with open('blog.txt', 'a', encoding='utf8') as fp: fp.write(title) 代码实现了我的 CSDN 博客首页所有博文标题的爬取,利用 requests 请求库发送请求,获取响应,用 XPath 获取每一篇博文的标题,然后写入 blog.txt 文件中: 123456789101112131415161718192021帝都的凛冬 最新屏蔽 CSDN 广告方法,专注阅读学习! 使用Github Pages和Hexo搭建自己的独立博客【超级详细的小白教程】 Python3 爬虫学习笔记 C08【解析库 Beautiful Soup】 Python3 爬虫学习笔记 C07 【解析库 lxml】 Python3 爬虫学习笔记 C06 【正则表达式】 Python3 爬虫学习笔记 C05 【Selenium + 无界面浏览器】 Python3 已经安装相关库,Pycharm 仍然报错 ModuleNotFoundError: No module named 'xxxxxx' 的解决办法 Windows/Android/iOS 等常见 User-Agent 大全 Selenium 显式等待条件及其含义 Python3 爬虫学习笔记 C04 【自动化测试工具 Selenium】 Python3 爬虫学习笔记 C03 【Ajax 数据爬取】 Python3 爬虫学习笔记 C02 【基本库 requests 的使用】 Python3 爬虫学习笔记 C01 【基本库 urllib 的使用】 利用官方支持为基于GitHub Pages的Hexo博客启用HTTPS 光学字符识别 Tesseract-OCR 的下载、安装和基本用法 Github+jsDelivr+PicGo 打造稳定快速、高效免费图床 利用Cloudflare为基于GitHub Pages的Hexo博客添加HTTPS支持 Python 中 if __name__ == '__main__': 的理解 Hexo 博客本地预览报错:Error: listen EADDRINUSE 0.0.0.0:4000 谷歌浏览器检查更新时出错:无法启动更新检查(错误代码为 3: 0x80080005 -- system level) 【9.1.2】打开方式open() 方法的第二个参数为打开方式,不同的打开方式如下: 读写方式 可否读写 若文件不存在 写入方式 r 读取 报错 不可写入 rb 以二进制方式读取 报错 不可写入 r+ 读取 + 写入 报错 覆盖写入 rb+ 以二进制方式读取+写入 报错 覆盖写入 w 写入 创建 覆盖写入 wb 以二进制方式写入 创建 覆盖写入 w+ 读取 + 写入 创建 覆盖写入 wb+ 以二进制方式读取+写入 创建 覆盖写入 a 写入 创建 附加写入 ab 以二进制方式写入 创建 附加写入 a+ 读取 + 写入 创建 附加写入 ab+ 以二进制方式读取+写入 创建 附加写入 【9.2】JSON 文件存储JSON,全称为 JavaScript Object Notation, 即 JavaScript 对象标记,它通过对象和数组的组合来表示数据,构造简洁但是结构化程度非常高,是一种轻量级的数据交换格式。 【9.2.1】对象和数组在 JavaScript 语言中,一切都是对象。因此,任何支持的类型都可以通过 JSON 来表示,例如字符串、数字、对象、数组等,但是对象和数组是比较特殊且常用的两种类型 对象:它在 JavaScript 中是使用花括号 {} 包裹起来的内容,数据结构为 {key1:value1, key2:value2, …} 的键值对结构。在面向对象的语言中,key 为对象的属性,value 为对应的值。键名可以使用整数和字符串来表示。值的类型可以是任意类型。 数组:数组在 JavaScript 中是方括号 [] 包裹起来的内容,数据结构为 [“java”, “javascript”, “vb”, …] 的索引结构。在 JavaScript 中,数组是一种比较特殊的数据类型,它也可以像对象那样使用键值对,但还是索引用得多。同样,值的类型可以是任意类型。 示例:一个 JSON 对象 123456789[{ \"name\": \"TRH\", \"gender\": \"male\", \"birthday\": \"1999-01-25\"}, { \"name\": \"XXX\", \"gender\": \"female\", \"birthday\": \"1999-10-18\"}] 【9.2.2】读取 JSONPython 里面的 JSON 库可以实现对 JSON 文件的读写操作,调用 JSON 库的 loads 方法将 JSON 文本字符串转为 JSON 对象、 dumps() 方法将 JSON 对象转为文本字符串 12345678910111213141516171819import jsonstr = '''[{ \"name\": \"TRH\", \"gender\": \"male\", \"birthday\": \"1999-01-25\"}, { \"name\": \"XXX\", \"gender\": \"female\", \"birthday\": \"1999-10-18\"}]'''data = json.loads(str)print(data)print(data[0]['name'])print(data[0].get('name'))print(data[0].get('age'))print(data[0].get('age', 25)) 使用 loads 方法将字符串转为 JSON 对象,通过索引来获取对应的内容,获取键值时有两种方式,一种是中括号加键名,另一种是通过 get 方法传入键名。使用 get 方法,如果键名不存在,则不会报错,会返回 None,get 方法还可以传入第二个参数(即默认值),尝试获取一个原字典中不存在的键名,此时默认会返回 None。如果传入第二个参数(即默认值),那么在不存在的情况下返回该默认值。 12345[{'name': 'TRH', 'gender': 'male', 'birthday': '1999-01-25'}, {'name': 'XXX', 'gender': 'female', 'birthday': '1999-10-18'}]TRHTRHNone25 【9.2.3】写入 JSON 文件调用 dumps 方法可以将 JSON 对象转化为字符串,然后再调用文件的 write 方法即可写入文本 123456789import jsondata = [{ 'name': 'TRH', 'gender': 'male', 'birthday': '1999-01-25'}]with open('data.json', 'w') as fp: fp.write(json.dumps(data)) data.json 文件: 1[{\"name\": \"TRH\", \"gender\": \"male\", \"birthday\": \"1999-01-25\"}] 添加参数 indent(代表缩进字符个数),将会格式化输出:123456789import jsondata = [{ 'name': 'TRH', 'gender': 'male', 'birthday': '1999-01-25'}]with open('data.json', 'w') as file: file.write(json.dumps(data, indent=2)) 输出结果: 1234567[ { \"name\": \"TRH\", \"gender\": \"male\", \"birthday\": \"1999-01-25\" }] 如果 JSON 中包含中文字符,需要指定参数 ensure_ascii 为 False,另外还要规定文件输出的编码: 123456789import jsondata = [{ 'name': '小明', 'gender': '男', 'birthday': '1999年01月25日'}]with open('data.json', 'w', encoding='utf-8') as file: file.write(json.dumps(data, indent=2, ensure_ascii=False)) 输出结果: 1234567[ { \"name\": \"小明\", \"gender\": \"男\", \"birthday\": \"1999年01月25日\" }] 【9.3】CSV 文本存储CSV(Comma-Separated Values)是逗号分隔值或字符分隔值的文件格式,其文件以纯文本的形式储存表格数据(数字和文本),CSV 文件的行与行之间用换行符分隔,列与列之间用逗号分隔 【9.3.1】写入12345678import csvwith open('data.csv', 'w') as csvfile: writer = csv.writer(csvfile) writer.writerow(['id', 'name', 'age']) writer.writerow(['10001', 'TRHX', 20]) writer.writerow(['10002', 'Bob', 22]) writer.writerow(['10003', 'Jordan', 21]) 打开 data.csv 文件,调用 CSV 库的 writer 方法初始化写入对象,然后调用 writerow 方法传入每行的数据即可完成写入,用 Excel 打开 data.csv 文件将是表格形式 1234567id,name,age10001,TRHX,2010002,Bob,2210003,Jordan,21 默认每一行之间是有一行空格的,可以使用参数 newline 来去除空行: 12345678import csvwith open('data.csv', 'w', newline='') as csvfile: writer = csv.writer(csvfile) writer.writerow(['id', 'name', 'age']) writer.writerow(['10001', 'TRHX', 20]) writer.writerow(['10002', 'Bob', 22]) writer.writerow(['10003', 'Jordan', 21]) 输出结果: 1234id,name,age10001,TRHX,2010002,Bob,2210003,Jordan,21 列与列之间的分隔符是可以修改的,只需要传入 delimiter 参数即可: 12345678import csvwith open('data.csv', 'w') as csvfile: writer = csv.writer(csvfile, delimiter=' ') writer.writerow(['id', 'name', 'age']) writer.writerow(['10001', 'TRHX', 20]) writer.writerow(['10002', 'Bob', 22]) writer.writerow(['10003', 'Jordan', 21]) 输出结果: 1234567id name age10001 TRHX 2010002 Bob 2210003 Jordan 21 调用 writerows 方法也可以同时写入多行,此时参数就需要为二维列表: 123456import csvwith open('data.csv', 'w') as csvfile: writer = csv.writer(csvfile, delimiter=' ') writer.writerow(['id', 'name', 'age']) writer.writerows([['10001', 'TRHX', 20], ['10002', 'Bob', 22], ['10003', 'Jordan', 21]]) 输出结果仍与原来的一样 此外 CSV 库中也提供了字典的写入方式: 123456789import csvwith open('data.csv', 'w') as csvfile: fieldnames = ['id', 'name', 'age'] writer = csv.DictWriter(csvfile, fieldnames=fieldnames) writer.writeheader() writer.writerow({'id': '10001', 'name': 'TRHX', 'age': 20}) writer.writerow({'id': '10002', 'name': 'Bob', 'age': 22}) writer.writerow({'id': '10003', 'name': 'Jordan', 'age': 21}) 首先定义 3 个字段,用 fieldnames 表示,然后将其传给 DictWriter 来初始化一个字典写入对象,接着可以调用 writeheader 方法先写入头信息,然后再调用 writerow 方法传入相应字典即可 1234567id,name,age10001,TRHX,2010002,Bob,2210003,Jordan,21 【9.3.2】读取有写入方法,同样也可以使用 csv 库来读取 CSV 文件: 123456import csvwith open('data.csv', 'r', encoding='utf-8') as csvfile: reader = csv.reader(csvfile) for row in reader: print(row) 构造 Reader 对象,遍历输出每行的内容,每一行都是一个列表形式。(如果 CSV 文件中包含中文的话,还需要指定文件编码)读取结果: 1234['id', 'name', 'age']['10001', 'TRHX', '20']['10002', 'Bob', '22']['10003', 'Jordan', '21'] 此外,还可以利用 pandas 的 read_csv 方法将数据从 CSV 中读取出来(pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。Pandas 纳入了大量库和一些标准的数据模型,提供了高效地操作大型数据集所需的工具) 1234import pandas as pddf = pd.read_csv('data.csv')print(df) 读取结果: 1234 id name age0 10001 TRHX 201 10002 Bob 222 10003 Jordan 21","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫学习","slug":"Python3-学习笔记/爬虫学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫学习/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"文件储存","slug":"文件储存","permalink":"https://www.itrhx.com/tags/文件储存/"}]},{"title":"Python3 爬虫学习笔记 C08","slug":"A38-Python3-spider-C08","date":"2019-08-26T13:57:58.870Z","updated":"2019-09-24T12:40:34.686Z","comments":true,"path":"2019/08/26/A38-Python3-spider-C08/","link":"","permalink":"https://www.itrhx.com/2019/08/26/A38-Python3-spider-C08/","excerpt":"Python3 爬虫学习笔记第八章 —— 【解析库 Beautiful Soup】","text":"Python3 爬虫学习笔记第八章 —— 【解析库 Beautiful Soup】 【8.1】关于 Beautiful SoupBeautiful Soup 可以从 HTML 或者 XML 文件中提取数据,Beautiful Soup 可以提供一些简单的、Python 式的函数用来处理导航、搜索、修改分析树等,它借助网页的结构和属性等特性来解析网页,lxml 只会局部遍历,而 Beautiful Soup 是基于 HTML DOM 的,会载入整个文档,解析整个 DOM 树,因此时间和内存开销都会大很多,所以性能要低于lxml 抓取工具 速度 使用难度 安装难度 正则 最快 困难 无(内置) lxml 快 简单 一般 BeautifulSoup 慢 最简单 简单 【8.2】Beautiful Soup 的基本使用需要使用命令 pip install bs4 安装库,Beautiful Soup 在解析时依赖解析器,除了支持 Python 标准库中的 HTML 解析器外,还支持一些第三方解析器: 解析器 使用方法 优势 劣势 Python 标准库 BeautifulSoup(markup, “html.parser”) Python 的内置标准库、执行速度适中 、文档容错能力强 Python 2.7.3 or 3.2.2) 前的版本中文容错能力差 LXML HTML 解析器 BeautifulSoup(markup, “lxml”) 速度快、文档容错能力强 需要安装 C 语言库 LXML XML 解析器 BeautifulSoup(markup, “xml”) 速度快、唯一支持 XML 的解析器 需要安装 C 语言库 html5lib BeautifulSoup(markup, “html5lib”) 最好的容错性、以浏览器的方式解析文档、生成 HTML5 格式的文档 速度慢、不依赖外部扩展 基本使用:1234from bs4 import BeautifulSoupsoup = BeautifulSoup('<p>Hello</p>', 'lxml')# soup = BeautifulSoup(open('soup.html', encoding='utf8'), 'lxml')print(soup.p.string) 输出结果:1Hello 【8.3】节点选择器直接调用节点的名称就可以选择节点元素,再调用 string 属性就可以得到节点内的文本 【8.3.1】元素选择新建 soup.html 文件:123456789101112131415161718192021222324252627282930313233343536<!DOCTYPE html><html lang=\"en\"><head> <meta charset=\"UTF-8\" /> <title>测试bs4</title></head><body><div> 甄姬 <p>百里守约</p> <p>李白</p> 太乙真人</div><div class=\"song\"> <p>李清照</p> <p>王安石</p> <p>苏轼</p> <p>柳宗元</p> <a href=\"http://www.song.com/\" title=\"赵匡义\" target=\"_self\">宋朝是最强大的王朝,不是军队的强大,而是经济很强大,国民都很有钱。</a> <img src=\"http://www.baidu.com/meinv.jpg\" alt=\"\"> <a href=\"\" class=\"du\">总为浮云能蔽日,长安不见使人愁</a></div><div class=\"tang\"> <ul> <li><a href=\"http://www.baidu.com\" title=\"qing\">清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村。</a> </li> <li><a href=\"http://www.163.com\" title=\"qin\">秦时明月汉时关,万里长征人未还,但使龙城飞将在,不教胡马度阴山。</a> </li> <li><a href=\"http://www.126.com\" alt=\"qi\">岐王宅里寻常见,崔九堂前几度闻,正是江南好风景,落花时节又逢君。</a> </li> <li><a href=\"http://www.sina.com\" class=\"du\">杜甫</a> </li> <li><b>唐朝</b></li> <li><i>宋朝</i></li> <li><a href=\"http://www.haha.com\" id=\"feng\">凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘。</a> </li> </ul></div></body></html> 1234567from bs4 import BeautifulSoupsoup = BeautifulSoup(open('soup.html', encoding='utf8'), 'lxml')print(soup.title)print(type(soup.title))print(soup.title.string)print(soup.head)print(soup.p) 依次查找 title、head、p 节点。输出结果:12345678<title>测试bs4</title><class 'bs4.element.Tag'>测试bs4<head><meta charset=\"utf-8\"/><title>测试bs4</title></head><p>百里守约</p> 【8.3.2】提取信息 string 属性:获取节点包含的文本值(如果标签里面还有标签,那么string获取到的结果为None) text 属性:获取节点包含的文本值 get_text() 属性:获取节点包含的文本值 name 属性:获取节点的名称 attrs :获取所有属性 attrs[‘属性名’] :获取指定属性 依然以 soup.html 为例:1234567891011121314from bs4 import BeautifulSoupsoup = BeautifulSoup(open('soup.html', encoding='utf8'), 'lxml')print(soup.title)print(soup.title.text)print(soup.title.get_text())print(soup.title.string)print(soup.div.string)print(soup.div.text)print(soup.title.name)print(soup.a['href']) # 获取href属性print(soup.a['title']) # 获取title属性print(soup.a['target']) # 获取target属性print(soup.a.attrs) # 获取所有属性print(soup.a.attrs['href']) # 获取href属性 输出结果:1234567891011121314151617<title>测试bs4</title>测试bs4测试bs4测试bs4None 甄姬 百里守约李白 太乙真人titlehttp://www.song.com/赵匡义_self{'href': 'http://www.song.com/', 'title': '赵匡义', 'target': '_self'}http://www.song.com/ 【8.3.3】嵌套选择12345678910from bs4 import BeautifulSouphtml = \"\"\"<html><head><title>This is a demo</title></head><body>\"\"\"soup = BeautifulSoup(html, 'lxml')print(soup.head.title)print(type(soup.head.title))print(soup.head.title.string) 获取 head 节点里面的 title 节点,输出结果:123<title>This is a demo</title><class 'bs4.element.Tag'>This is a demo 【8.3.4】关联选择 contents 属性:获取某个节点元素的直接子节点 children 属性:遍历某个节点元素的子节点 descendants 属性:获取某个节点元素所有的子孙节点 parent 属性:获取某个节点元素的父节点 parents 属性:获取某个节点元素所有的祖先节点 next_sibling 属性:获取节点的下一个兄弟元素 previous_sibling 属性:获取节点的上一个兄弟元素 next_siblings 属性:获取某个节点所有后面的兄弟元素 previous_siblings 属性:获取某个节点所有前面的兄弟元素 contents 属性应用示例 12345678910111213141516171819202122from bs4 import BeautifulSouphtml = \"\"\"<html> <head> <title>The Dormouse's story</title> </head> <body> <p class=\"story\"> Once upon a time there were three little sisters; and their names were <a href=\"http://example.com/elsie\" class=\"sister\" id=\"link1\"> <span>Elsie</span> </a> <a href=\"http://example.com/lacie\" class=\"sister\" id=\"link2\">Lacie</a> and <a href=\"http://example.com/tillie\" class=\"sister\" id=\"link3\">Tillie</a> and they lived at the bottom of a well. </p> <p class=\"story\">...</p>\"\"\"soup = BeautifulSoup(html, 'lxml')print(soup.p.contents) 获取 p 节点元素的直接子节点,输出结果:123['\\n Once upon a time there were three little sisters; and their names were\\n ', <a class=\"sister\" href=\"http://example.com/elsie\" id=\"link1\"><span>Elsie</span></a>, '\\n', <a class=\"sister\" href=\"http://example.com/lacie\" id=\"link2\">Lacie</a>, ' \\n and\\n ', <a class=\"sister\" href=\"http://example.com/tillie\" id=\"link3\">Tillie</a>, '\\n and they lived at the bottom of a well.\\n '] children 属性应用示例: 123456789101112131415161718192021222324from bs4 import BeautifulSouphtml = \"\"\"<html> <head> <title>The Dormouse's story</title> </head> <body> <p class=\"story\"> Once upon a time there were three little sisters; and their names were <a href=\"http://example.com/elsie\" class=\"sister\" id=\"link1\"> <span>Elsie</span> </a> <a href=\"http://example.com/lacie\" class=\"sister\" id=\"link2\">Lacie</a> and <a href=\"http://example.com/tillie\" class=\"sister\" id=\"link3\">Tillie</a> and they lived at the bottom of a well. </p> <p class=\"story\">...</p>\"\"\"soup = BeautifulSoup(html, 'lxml')print(soup.p.children)for i, child in enumerate(soup.p.children): print(i, child) 遍历 p 节点元素的子节点,输出结果:12345678910111213141516<list_iterator object at 0x00000228E3C205F8>0 Once upon a time there were three little sisters; and their names were 1 <a class=\"sister\" href=\"http://example.com/elsie\" id=\"link1\"><span>Elsie</span></a>2 3 <a class=\"sister\" href=\"http://example.com/lacie\" id=\"link2\">Lacie</a>4 and 5 <a class=\"sister\" href=\"http://example.com/tillie\" id=\"link3\">Tillie</a>6 and they lived at the bottom of a well. descendants 属性应用示例:123456789101112131415161718192021222324from bs4 import BeautifulSouphtml = \"\"\"<html> <head> <title>The Dormouse's story</title> </head> <body> <p class=\"story\"> Once upon a time there were three little sisters; and their names were <a href=\"http://example.com/elsie\" class=\"sister\" id=\"link1\"> <span>Elsie</span> </a> <a href=\"http://example.com/lacie\" class=\"sister\" id=\"link2\">Lacie</a> and <a href=\"http://example.com/tillie\" class=\"sister\" id=\"link3\">Tillie</a> and they lived at the bottom of a well. </p> <p class=\"story\">...</p>\"\"\"soup = BeautifulSoup(html, 'lxml')print(soup.p.descendants)for i, child in enumerate(soup.p.descendants): print(i, child)获取 p 节点元素所有的子孙节点,输出结果:123456789101112131415161718192021222324<generator object descendants at 0x0000018404A4C3B8>0 Once upon a time there were three little sisters; and their names were 1 <a class=\"sister\" href=\"http://example.com/elsie\" id=\"link1\"><span>Elsie</span></a>2 3 <span>Elsie</span>4 Elsie5 6 7 <a class=\"sister\" href=\"http://example.com/lacie\" id=\"link2\">Lacie</a>8 Lacie9 and 10 <a class=\"sister\" href=\"http://example.com/tillie\" id=\"link3\">Tillie</a>11 Tillie12 and they lived at the bottom of a well.parent 属性应用示例:123456789101112131415161718from bs4 import BeautifulSouphtml = \"\"\"<html> <head> <title>The Dormouse's story</title> </head> <body> <p class=\"story\"> Once upon a time there were three little sisters; and their names were <a href=\"http://example.com/elsie\" class=\"sister\" id=\"link1\"> <span>Elsie</span> </a> </p> </body></html>\"\"\"soup = BeautifulSoup(html, 'lxml')print(soup.a.parent)获取 a 节点元素的父节点,输出结果:123456<p class=\"story\"> Once upon a time there were three little sisters; and their names were <a class=\"sister\" href=\"http://example.com/elsie\" id=\"link1\"><span>Elsie</span></a></p>parents 属性应用示例:1234567891011121314from bs4 import BeautifulSouphtml = \"\"\"<html> <body> <p class=\"story\"> <a href=\"http://example.com/elsie\" class=\"sister\" id=\"link1\"> <span>Elsie</span> </a> </p>\"\"\"soup = BeautifulSoup(html, 'lxml')print(type(soup.a.parents))print(list(enumerate(soup.a.parents)))获取 a 节点元素所有的祖先节点,输出结果:1234567891011121314151617181920212223242526<class 'generator'>[(0, <p class=\"story\"><a class=\"sister\" href=\"http://example.com/elsie\" id=\"link1\"><span>Elsie</span></a></p>), (1, <body><p class=\"story\"><a class=\"sister\" href=\"http://example.com/elsie\" id=\"link1\"><span>Elsie</span></a></p></body>), (2, <html><body><p class=\"story\"><a class=\"sister\" href=\"http://example.com/elsie\" id=\"link1\"><span>Elsie</span></a></p></body></html>), (3, <html><body><p class=\"story\"><a class=\"sister\" href=\"http://example.com/elsie\" id=\"link1\"><span>Elsie</span></a></p></body></html>)]next_sibling、previous_sibling、next_siblings、previous_siblings 属性应用示例: 1234567891011121314151617181920212223html = \"\"\"<html> <body> <p class=\"story\"> Once upon a time there were three little sisters; and their names were <a href=\"http://example.com/elsie\" class=\"sister\" id=\"link1\"> <span>Elsie</span> </a> Hello <a href=\"http://example.com/lacie\" class=\"sister\" id=\"link2\">Lacie</a> and <a href=\"http://example.com/tillie\" class=\"sister\" id=\"link3\">Tillie</a> and they lived at the bottom of a well. </p> </body></html>\"\"\"from bs4 import BeautifulSoupsoup = BeautifulSoup(html, 'lxml')print('Next Sibling', soup.a.next_sibling)print('Prev Sibling', soup.a.previous_sibling)print('Next Siblings', list(enumerate(soup.a.next_siblings)))print('Prev Siblings', list(enumerate(soup.a.previous_siblings))) next_sibling 和 previous_sibling 分别获取 a 节点的下一个和上一个兄弟元素,next_siblings 和 previous_siblings 则分别返回 a 节点后面和前面的兄弟节点,输出结果: 12345678Next Sibling Hello Prev Sibling Once upon a time there were three little sisters; and their names were Next Siblings [(0, '\\n Hello\\n '), (1, <a class=\"sister\" href=\"http://example.com/lacie\" id=\"link2\">Lacie</a>), (2, ' \\n and\\n '), (3, <a class=\"sister\" href=\"http://example.com/tillie\" id=\"link3\">Tillie</a>), (4, '\\n and they lived at the bottom of a well.\\n ')]Prev Siblings [(0, '\\n Once upon a time there were three little sisters; and their names were\\n ')] 【8.4】方法选择器节点选择器直接调用节点的名称就可以选择节点元素,如果进行比较复杂的选择的话,方法选择器是一个不错的选择,它更灵活,常见的方法有 find_all、find 等,调用它们,直接传入相应的参数,就可以灵活查询了。 【8.4.1】find_all() 方法find_all 方法可以查询所有符合条件的元素,给它传入一些属性或文本来得到符合条件的元素。find_all 方法的 API:find_all(name , attrs , recursive , text , **kwargs)新建 soup.html:123456789101112131415161718192021222324252627282930313233343536<!DOCTYPE html><html lang=\"en\"><head> <meta charset=\"UTF-8\" /> <title>测试bs4</title></head><body><div> 甄姬 <p>百里守约</p> <p>李白</p> 太乙真人</div><div class=\"song\"> <p>李清照</p> <p>王安石</p> <p>苏轼</p> <p>柳宗元</p> <a href=\"http://www.song.com/\" title=\"赵匡义\" target=\"_self\">宋朝是最强大的王朝,不是军队的强大,而是经济很强大,国民都很有钱。</a> <img src=\"http://www.baidu.com/meinv.jpg\" alt=\"\"> <a href=\"\" class=\"du\">总为浮云能蔽日,长安不见使人愁</a></div><div class=\"tang\"> <ul> <li><a href=\"http://www.baidu.com\" title=\"qing\">清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村。</a> </li> <li><a href=\"http://www.163.com\" title=\"qin\">秦时明月汉时关,万里长征人未还,但使龙城飞将在,不教胡马度阴山。</a> </li> <li><a href=\"http://www.126.com\" name=\"qi\">岐王宅里寻常见,崔九堂前几度闻,正是江南好风景,落花时节又逢君。</a> </li> <li><a href=\"http://www.sina.com\" class=\"du\">杜甫</a> </li> <li><b>唐朝</b></li> <li><i>宋朝</i></li> <li><a href=\"http://www.haha.com\" id=\"feng\">凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘。</a> </li> </ul></div></body></html> 示例代码:12345678910from bs4 import BeautifulSoupsoup = BeautifulSoup(open('soup.html', encoding='utf8'), 'lxml')print(soup.find_all('a'), '\\n')print(soup.find_all('a')[1], '\\n')print(soup.find_all('a')[1].text, '\\n')print(soup.find_all(['a', 'b', 'i']), '\\n')print(soup.find_all('a', limit=2), '\\n')print(soup.find_all(title='qing'), '\\n')print(soup.find_all(attrs={'id': 'feng'}), '\\n') 输出结果:12345678910111213[<a href=\"http://www.song.com/\" target=\"_self\" title=\"赵匡义\">宋朝是最强大的王朝,不是军队的强大,而是经济很强大,国民都很有钱。</a>, <a class=\"du\" href=\"\">总为浮云能蔽日,长安不见使人愁</a>, <a href=\"http://www.baidu.com\" title=\"qing\">清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村。</a>, <a href=\"http://www.163.com\" title=\"qin\">秦时明月汉时关,万里长征人未还,但使龙城飞将在,不教胡马度阴山。</a>, <a href=\"http://www.126.com\" name=\"qi\">岐王宅里寻常见,崔九堂前几度闻,正是江南好风景,落花时节又逢君。</a>, <a class=\"du\" href=\"http://www.sina.com\">杜甫</a>, <a href=\"http://www.haha.com\" id=\"feng\">凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘。</a>] <a class=\"du\" href=\"\">总为浮云能蔽日,长安不见使人愁</a> 总为浮云能蔽日,长安不见使人愁 [<a href=\"http://www.song.com/\" target=\"_self\" title=\"赵匡义\">宋朝是最强大的王朝,不是军队的强大,而是经济很强大,国民都很有钱。</a>, <a class=\"du\" href=\"\">总为浮云能蔽日,长安不见使人愁</a>, <a href=\"http://www.baidu.com\" title=\"qing\">清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村。</a>, <a href=\"http://www.163.com\" title=\"qin\">秦时明月汉时关,万里长征人未还,但使龙城飞将在,不教胡马度阴山。</a>, <a href=\"http://www.126.com\" name=\"qi\">岐王宅里寻常见,崔九堂前几度闻,正是江南好风景,落花时节又逢君。</a>, <a class=\"du\" href=\"http://www.sina.com\">杜甫</a>, <b>唐朝</b>, <i>宋朝</i>, <a href=\"http://www.haha.com\" id=\"feng\">凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘。</a>] [<a href=\"http://www.song.com/\" target=\"_self\" title=\"赵匡义\">宋朝是最强大的王朝,不是军队的强大,而是经济很强大,国民都很有钱。</a>, <a class=\"du\" href=\"\">总为浮云能蔽日,长安不见使人愁</a>] [<a href=\"http://www.baidu.com\" title=\"qing\">清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村。</a>] [<a href=\"http://www.haha.com\" id=\"feng\">凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘。</a>] 【8.4.2】find() 方法find() 方法使用方法与 find_all() 方法相同,不同的是,find 方法返回的是单个元素,也就是第一个匹配的元素,而 find_all 返回的是所有匹配的元素组成的列表特别的: find_parents 和 find_parent:前者返回所有祖先节点,后者返回直接父节点。 find_next_siblings 和 find_next_sibling:前者返回后面所有的兄弟节点,后者返回后面第一个兄弟节点。 find_previous_siblings 和 find_previous_sibling:前者返回前面所有的兄弟节点,后者返回前面第一个兄弟节点。 find_all_next 和 find_next:前者返回节点后所有符合条件的节点,后者返回第一个符合条件的节点。 find_all_previous 和 find_previous:前者返回节点前所有符合条件的节点,后者返回第一个符合条件的节点。 【8.5】CSS 选择器使用 CSS 选择器,只需要调用 select 方法,传入相应的 CSS 选择器即可新建 soup.html 文件:1234567891011121314<!DOCTYPE html><html lang=\"en\"><div class=\"tang\"> <ul> <li><a href=\"http://www.baidu.com\" title=\"qing\">清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村。</a> </li> <li><a href=\"http://www.163.com\" title=\"qin\">秦时明月汉时关,万里长征人未还,但使龙城飞将在,不教胡马度阴山。</a> </li> <li><a href=\"http://www.126.com\" name=\"qi\">岐王宅里寻常见,崔九堂前几度闻,正是江南好风景,落花时节又逢君。</a> </li> <li><a href=\"http://www.sina.com\" class=\"du\">杜甫</a> </li> <li><a href=\"http://www.haha.com\" id=\"feng\">凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘。</a> </li> </ul></div></body></html> 通过 CSS 选择器依次选择 class=”tang” 的 div 节点下的 a 节点、id 为 feng 的节点以及其 href 元素:1234567from bs4 import BeautifulSoupsoup = BeautifulSoup(open('soup.html', encoding='utf8'), 'lxml')print(soup.select('li'), '\\n')print(soup.select('.tang > ul > li > a')[2], '\\n')print(soup.select('#feng')[0].text, '\\n')print(soup.select('#feng')[0]['href'], '\\n') 输出结果:1234567[<li><a href=\"http://www.baidu.com\" title=\"qing\">清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村。</a> </li>, <li><a href=\"http://www.163.com\" title=\"qin\">秦时明月汉时关,万里长征人未还,但使龙城飞将在,不教胡马度阴山。</a> </li>, <li><a href=\"http://www.126.com\" name=\"qi\">岐王宅里寻常见,崔九堂前几度闻,正是江南好风景,落花时节又逢君。</a> </li>, <li><a class=\"du\" href=\"http://www.sina.com\">杜甫</a> </li>, <li><a href=\"http://www.haha.com\" id=\"feng\">凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘。</a> </li>] <a href=\"http://www.126.com\" name=\"qi\">岐王宅里寻常见,崔九堂前几度闻,正是江南好风景,落花时节又逢君。</a> 凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘。 http://www.haha.com 附表:CSS 选择器,来源:https://www.w3school.com.cn/cssref/css_selectors.asp 选择器 例子 例子描述 CSS .class .intro 选择 class=”intro” 的所有元素 1 #id #firstname 选择 id=”firstname” 的所有元素 1 * * 选择所有元素 2 element p 选择所有 元素 1 element,element div,p 选择所有 元素和所有 元素 1 element element div p 选择 元素内部的所有 元素 1 element>element div>p 选择父元素为 元素的所有 元素 2 element+element div+p 选择紧接在 元素之后的所有 元素 2 [attribute] [target] 选择带有 target 属性所有元素 2 [attribute=value] [target=_blank] 选择 target=”_blank” 的所有元素 2 [attribute~=value] [title~=flower] 选择 title 属性包含单词 “flower” 的所有元素 2 [attribute =value] [lang =en] 选择 lang 属性值以 “en” 开头的所有元素 2 :link a:link 选择所有未被访问的链接 1 :visited a:visited 选择所有已被访问的链接 1 :active a:active 选择活动链接 1 :hover a:hover 选择鼠标指针位于其上的链接 1 :focus input:focus 选择获得焦点的 input 元素 2 :first-letter p:first-letter 选择每个 元素的首字母 1 :first-line p:first-line 选择每个 元素的首行 1 :first-child p:first-child 选择属于父元素的第一个子元素的每个 元素 2 :before p:before 在每个 元素的内容之前插入内容 2 :after p:after 在每个 元素的内容之后插入内容 2 :lang(language) p:lang(it) 选择带有以 “it” 开头的 lang 属性值的每个 元素 2 element1~element2 p~ul 选择前面有 元素的每个 元素 3 [attribute^=value] a[src^=”https”] 选择其 src 属性值以 “https” 开头的每个 元素 3 [attribute$=value] a[src$=”.pdf”] 选择其 src 属性以 “.pdf” 结尾的所有 元素 3 [attribute*=value] a[src*=”abc”] 选择其 src 属性中包含 “abc” 子串的每个 元素 3 :first-of-type p:first-of-type 选择属于其父元素的首个 元素的每个 元素 3 :last-of-type p:last-of-type 选择属于其父元素的最后 元素的每个 元素 3 :only-of-type p:only-of-type 选择属于其父元素唯一的 元素的每个 元素 3 :only-child p:only-child 选择属于其父元素的唯一子元素的每个 元素 3 :nth-child(n) p:nth-child(2) 选择属于其父元素的第二个子元素的每个 元素 3 :nth-last-child(n) p:nth-last-child(2) 同上,从最后一个子元素开始计数 3 :nth-of-type(n) p:nth-of-type(2) 选择属于其父元素第二个 元素的每个 元素 3 :nth-last-of-type(n) p:nth-last-of-type(2) 同上,但是从最后一个子元素开始计数 3 :last-child p:last-child 选择属于其父元素最后一个子元素每个 元素 3 :root :root 选择文档的根元素 3 :empty p:empty 选择没有子元素的每个 元素(包括文本节点) 3 :target #news:target 选择当前活动的 #news 元素 3 :enabled input:enabled 选择每个启用的 元素 3 :disabled input:disabled 选择每个禁用的 元素 3 :checked input:checked 选择每个被选中的 元素 3 :not(selector) :not(p) 选择非 元素的每个元素 3 ::selection ::selection 选择被用户选取的元素部分 3 【8.6】附表:Beautiful Soup 库 soup 对象常用属性与方法 基本元素 说明 返回类型 tag soup.a bs4.element.Tag name soup.a.name str attrs soup.a.attrs dict contents 子节点 list children 遍历子节点 list_iterator descendants 遍历所有子孙节点 generator parent 返回父亲标签 bs4.element.Tag parents 上行遍历父辈标签 generator prettify() 添加/n str find_all(name,attr) soup.find_all(‘a’)/([‘a’,‘b’])/(True)/(‘p’,‘course’)/(id=‘link1’)/(string=‘python’) bs4.element.ResultSet find() soup.find(‘a’)/返回第一个a标签 bs4.element.Tag","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫学习","slug":"Python3-学习笔记/爬虫学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫学习/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"Beautiful Soup","slug":"Beautiful-Soup","permalink":"https://www.itrhx.com/tags/Beautiful-Soup/"}]},{"title":"Python3 爬虫学习笔记 C07","slug":"A37-Python3-spider-C07","date":"2019-08-25T11:31:18.872Z","updated":"2019-09-24T12:40:30.940Z","comments":true,"path":"2019/08/25/A37-Python3-spider-C07/","link":"","permalink":"https://www.itrhx.com/2019/08/25/A37-Python3-spider-C07/","excerpt":"Python3 爬虫学习笔记第七章 —— 【解析库 lxml】","text":"Python3 爬虫学习笔记第七章 —— 【解析库 lxml】 【7.1】关于 lxml lxml 是 Python 的一个解析库,支持 HTML 和 XML 的解析,支持 XPath 解析方式,解析效率非常高,使用前需要用命令 pip3 install lxml 安装 lxml 库 【7.2】使用 XPath XPath(XML Path Language)即 XML 路径语言, lxml 解析库使用的正是 XPath 语法,最初是用来搜寻 XML 文档的,是一门在 XML 文档中查找信息的语言,它同样适用于 HTML 文档的搜索 XPath 常用规则 表达式 描述 nodename 选取此节点的所有子节点 / 从当前节点选取直接子节点 // 从当前节点选取子孙节点 . 选取当前节点 .. 选取当前节点的父节点 @ 选取属性 * 通配符,选择所有元素节点与元素名 @* 选取所有属性 [@attrib] 选取具有给定属性的所有元素 [@attrib=’value’] 选取给定属性具有给定值的所有元素 [tag] 选取所有具有指定元素的直接子节点 [tag=’text’] 选取所有具有指定元素并且文本内容是text节点 浏览器插件 XPath Helper,在线验证 XPath,谷歌商店下载地址:https://chrome.google.com/webstore/detail/hgimnogjllphhhkhlmebbmlgjoejdpjl XPath 基本使用方法:首先使用代码 from lxml import etree导入库,然后将 HTML 文档变成一个对象,再调用对象的方法去查找指定的节点,方法有两种:tree = etree.parse() 为本地文件查找,tree = etree.HTML() 为网络文件查找,再使用语句 tree.xpath() 查找指定节点。 【7.3】查找所有节点 新建一个 xpath.html 本地文件,内容如下: 123456789101112131415161718192021222324252627282930313233 <!DOCTYPE html><html lang=\"en\"><head> <meta charset=\"UTF-8\" /> <title>xpath测试</title></head><body><div class=\"song\"> 火药 <b>指南针</b> <b>印刷术</b> 造纸术</div><div class=\"tang\"> <ul> <li class=\"balove\">停车坐爱枫林晚,霜叶红于二月花。</li> <li id=\"hua\">商女不知亡国恨,隔江犹唱后庭花。</li> <li class=\"love\" name=\"yang\">一骑红尘妃子笑,无人知是荔枝来。</li> <li id=\"bei\">葡萄美酒夜光杯,欲饮琵琶马上催。</li> <li><a href=\"http://www.baidu.com/\">百度一下</a> </li> </ul> <ol> <li class=\"balucy\">寻寻觅觅冷冷清清,凄凄惨惨戚戚。</li> <li class=\"lily\">咋暖还寒时候,最难将息。</li> <li class=\"lilei\">三杯两盏淡酒。</li> <li>怎敌他晚来风急。</li> <li>雁过也,正伤心,却是旧时相识。</li> <li>爱情三十六计</li> <li>什么是爱情</li> </ol></div></body></html> 查找所有节点:12345from lxml import etreehtml = etree.parse('./xpath.html')result = html.xpath('//*')print(result) 使用 * 代表匹配所有节点,整个 xpath.html 文件中的所有节点都会被获取到,返回形式是一个列表,每个元素是 Element 类型,其后跟了节点的名称,如 html、body、div、ul、li、a 等,所有节点都包含在列表中,输出结果如下:1[<Element html at 0x1a836a34508>, <Element head at 0x1a836a344c8>, <Element meta at 0x1a836a345c8>, <Element title at 0x1a836a34608>, <Element body at 0x1a836a34648>, <Element div at 0x1a836a346c8>, <Element b at 0x1a836a34708>, <Element b at 0x1a836a34748>, <Element div at 0x1a836a34788>, <Element ul at 0x1a836a34688>, <Element li at 0x1a836a347c8>, <Element li at 0x1a836a34808>, <Element li at 0x1a836a34848>, <Element li at 0x1a836a34888>, <Element li at 0x1a836a348c8>, <Element a at 0x1a836a34908>, <Element ol at 0x1a836a34948>, <Element li at 0x1a836a34988>, <Element li at 0x1a836a349c8>, <Element li at 0x1a836a34a08>, <Element li at 0x1a836a34a48>, <Element li at 0x1a836a34a88>, <Element li at 0x1a836a34ac8>, <Element li at 0x1a836a34b08>] 【7.4】查找子节点 通过 / 或 // 即可查找元素的子节点或子孙节点: 12345 from lxml import etreehtml = etree.parse('./xpath.html')result = html.xpath('//ul/li')print(result) 选择 ul 节点的所有直接 li 子节点:1[<Element li at 0x2a094d044c8>, <Element li at 0x2a094d045c8>, <Element li at 0x2a094d04608>, <Element li at 0x2a094d04648>, <Element li at 0x2a094d04688>] 【7.5】查找父节点 知道了子节点,也可以用 .. 或者 parent:: 查找其父节点 12345 from lxml import etreehtml = etree.parse('./xpath.html')result = html.xpath('//ol/../@class')print(result) 12345from lxml import etreehtml = etree.parse('./xpath.html')result = html.xpath('//ol/parent::*/@class')print(result) 先查找到 ol 节点,随后获取其父节点以及其 class 属性:1['tang'] 【7.6】属性匹配 有时候 HTML 包含多个相同名的节点,而节点的属性是不一样的,此时可以用 @ 符号进行属性过滤 12345 from lxml import etreehtml = etree.parse('./xpath.html')result = html.xpath('//li[@class=\"balucy\"]')print(result) xpath.html 文件中,只有一个 class 为 balucy 的节点:<li class="balucy">寻寻觅觅冷冷清清,凄凄惨惨戚戚。</li>,运行以上代码将返回一个该元素:1[<Element li at 0x16e53aa54c8>] 【7.7】文本获取 使用 text() 方法即可提取节点中的文本: 12345 from lxml import etreehtml = etree.parse('./xpath.html')result = html.xpath('//li[@class=\"balucy\"]/text()')print(result) 输出结果:1['寻寻觅觅冷冷清清,凄凄惨惨戚戚。'] 再次观察 xpath.html 文件中的 <ol></ol>这一部分:123456789<ol> <li class=\"balucy\">寻寻觅觅冷冷清清,凄凄惨惨戚戚。</li> <li class=\"lily\">咋暖还寒时候,最难将息。</li> <li class=\"lilei\">三杯两盏淡酒。</li> <li>怎敌他晚来风急。</li> <li>雁过也,正伤心,却是旧时相识。</li> <li>爱情三十六计</li> <li>什么是爱情</li></ol> 如果我们想要提取 <li> 节点里面所有的文本,就可以使用 html.xpath('//ol/li/text()') 语句:12345from lxml import etreehtml = etree.parse('./xpath.html')result = html.xpath('//ol/li/text()')print(result) 输出结果:1['寻寻觅觅冷冷清清,凄凄惨惨戚戚。', '咋暖还寒时候,最难将息。', '三杯两盏淡酒。', '怎敌他晚来风急。', '雁过也,正伤心,却是旧时相识。', '爱情三十六计', '什么是爱情'] 同样还有另一种方法,使用 html.xpath('//ol//text()') 语句,// 将会选取所有子孙节点的文本,<ol> 和 <li> 节点下的换行符也将被提取出来:12345from lxml import etreehtml = etree.parse('./xpath.html')result = html.xpath('//ol//text()')print(result) 输出结果:1['\\n ', '寻寻觅觅冷冷清清,凄凄惨惨戚戚。', '\\n ', '咋暖还寒时候,最难将息。', '\\n ', '三杯两盏淡酒。', '\\n ', '怎敌他晚来风急。', '\\n ', '雁过也,正伤心,却是旧时相识。', '\\n ', '爱情三十六计', '\\n ', '什么是爱情', '\\n '] 【7.8】属性获取 与属性匹配一样,属性获取仍然使用 @: 12345 from lxml import etreehtml = etree.parse('./xpath.html')result = html.xpath('//ul/li[5]/a/@href')print(result) 获取 href 属性: 1['http://www.baidu.com/'] 【7.9】一个属性包含多个值的匹配某个节点的某个属性可能有多个值,例如:1<li class=\"li li-first\"><a href=\"link.html\">first item</a></li> li 节点的 class 属性有 li 和 li-first 两个值,如果使用 html.xpath('//li[@class="li"] 语句,将无法成功匹配,这时就需要使用 contains 方法了,第一个参数传入属性名称,第二个参数传入属性值,只要此属性包含所传入的属性值,就可以完成匹配了 1234567from lxml import etreetext = ''' <li class=\"li li-first\"><a href=\"link.html\">first item</a></li> '''html = etree.HTML(text)result = html.xpath('//li[contains(@class, \"li\")]/a/text()')print(result) 输出结果:1['first item'] 【7.10】多个属性匹配一个节点XPath 还可以根据多个属性来确定一个节点,这时就需要同时匹配多个属性。此时可以使用运算符 and 来连接:1234567from lxml import etreetext = ''' <li class=\"li\" name=\"item\"><a href=\"link.html\">first item</a></li>'''html = etree.HTML(text)result = html.xpath('//li[@class=\"li\" and @name=\"item\"]/a/text()')print(result) 输出结果:1['first item'] 示例中运用了运算符 and 来连接,此外常见的运算符如下: 运算符 描述 实例 返回值 or 或 age=19 or age=20 如果 age 是 19 或者 20,则返回 true。如果 age 是其他值,则返回 false and 与 age>19 and age<21 如果 age 大于 19 且小于 21,则返回 true。如果 age 是其他值,则返回 false mod 计算除法的余数 5 mod 2 1 | 计算两个节点集 //book | //cd 返回所有拥有 book 和 cd 元素的节点集 + 加法 10 + 5 15 - 减法 10 - 5 5 * 乘法 10 * 5 50 div 除法 10 div 5 2 = 等于 age=19 如果 age 是 19,则返回 true。如果 age 不是 19,则返回 false != 不等于 age!=19 如果 age 不是 19,则返回 true。如果 age 是 19,则返回 false < 小于 age<19 如果 age 小于 19,则返回 true。如果 age 不小于 19,则返回 false <= 小于或等于 age<=19 如果 age 小于等于 19,则返回 true。如果 age 大于 19,则返回 false > 大于 age>19 如果 age 大于 19,则返回 true。如果 age 不大于 19,则返回 false >= 大于或等于 age>=19 如果 age 大于等于 19,则返回 true。如果 age 小于 19,则返回 false 【7.11】按顺序选择节点某些属性可能同时匹配了多个节点,如果要选择其中几个节点,可以利用中括号传入索引的方法获取特定次序的节点12345678910111213141516171819202122from lxml import etreetext = '''<div> <ul> <li class=\"item-0\"><a href=\"link1.html\">first item</a></li> <li class=\"item-1\"><a href=\"link2.html\">second item</a></li> <li class=\"item-inactive\"><a href=\"link3.html\">third item</a></li> <li class=\"item-1\"><a href=\"link4.html\">fourth item</a></li> <li class=\"item-0\"><a href=\"link5.html\">fifth item</a> </ul> </div>'''html = etree.HTML(text)result = html.xpath('//li[1]/a/text()')print(result)result = html.xpath('//li[last()]/a/text()')print(result)result = html.xpath('//li[position()<3]/a/text()')print(result)result = html.xpath('//li[last()-2]/a/text()')print(result) li[1]:选取第一个 li 节点; li[last()]:选取最后一个 li 节点; position()<3:选取位置小于 3 的 li 节点; li[last()-2]:选取倒数第三个 li 节点 输出结果:1234['first item']['fifth item']['first item', 'second item']['third item'] 【7.12】节点轴选择节点轴选择:获取子元素、兄弟元素、父元素、祖先元素等12345678910111213141516171819202122232425262728from lxml import etreetext = '''<div> <ul> <li class=\"item-0\"><a href=\"link1.html\"><span>first item</span></a></li> <li class=\"item-1\"><a href=\"link2.html\">second item</a></li> <li class=\"item-inactive\"><a href=\"link3.html\">third item</a></li> <li class=\"item-1\"><a href=\"link4.html\">fourth item</a></li> <li class=\"item-0\"><a href=\"link5.html\">fifth item</a> </ul> </div>'''html = etree.HTML(text)result = html.xpath('//li[1]/ancestor::*')print(result)result = html.xpath('//li[1]/ancestor::div')print(result)result = html.xpath('//li[1]/attribute::*')print(result)result = html.xpath('//li[1]/child::a[@href=\"link1.html\"]')print(result)result = html.xpath('//li[1]/descendant::span')print(result)result = html.xpath('//li[1]/following::*[2]')print(result)result = html.xpath('//li[1]/following-sibling::*')print(result) 输出结果:1234567[<Element html at 0x1d3749e9548>, <Element body at 0x1d3749e94c8>, <Element div at 0x1d3749e9488>, <Element ul at 0x1d3749e9588>][<Element div at 0x1d3749e9488>]['item-0'][<Element a at 0x1d3749e9588>][<Element span at 0x1d3749e9488>][<Element a at 0x1d3749e9588>][<Element li at 0x1d3749e94c8>, <Element li at 0x1d3749e95c8>, <Element li at 0x1d3749e9608>, <Element li at 0x1d3749e9648>] 基本语法:轴名称::节点测试[谓语] 轴名称对应的结果: 轴名称 结果 ancestor 选取当前节点的所有先辈(父、祖父等) ancestor-or-self 选取当前节点的所有先辈(父、祖父等)以及当前节点本身 attribute 选取当前节点的所有属性 child 选取当前节点的所有子元素 descendant 选取当前节点的所有后代元素(子、孙等) descendant-or-self 选取当前节点的所有后代元素(子、孙等)以及当前节点本身 following 选取文档中当前节点的结束标签之后的所有节点 namespace 选取当前节点的所有命名空间节点 parent 选取当前节点的父节点 preceding 选取文档中当前节点的开始标签之前的所有节点 preceding-sibling 选取当前节点之前的所有同级节点 self 选取当前节点 实例: 例子 结果 child::book 选取所有属于当前节点的子元素的 book 节点 attribute::lang 选取当前节点的 lang 属性 child::* 选取当前节点的所有子元素 attribute::* 选取当前节点的所有属性 child::text() 选取当前节点的所有文本子节点 child::node() 选取当前节点的所有子节点 descendant::book 选取当前节点的所有 book 后代 ancestor::book 选择当前节点的所有 book 先辈 ancestor-or-self::book 选取当前节点的所有 book 先辈以及当前节点(如果此节点是 book 节点) child::*/child::price 选取当前节点的所有 price 孙节点","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫学习","slug":"Python3-学习笔记/爬虫学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫学习/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"lxml","slug":"lxml","permalink":"https://www.itrhx.com/tags/lxml/"},{"name":"XPath","slug":"XPath","permalink":"https://www.itrhx.com/tags/XPath/"}]},{"title":"Python3 爬虫学习笔记 C06","slug":"A36-Python3-spider-C06","date":"2019-08-24T10:37:05.278Z","updated":"2019-09-24T12:40:18.770Z","comments":true,"path":"2019/08/24/A36-Python3-spider-C06/","link":"","permalink":"https://www.itrhx.com/2019/08/24/A36-Python3-spider-C06/","excerpt":"Python3 爬虫学习笔记第六章 —— 【正则表达式】","text":"Python3 爬虫学习笔记第六章 —— 【正则表达式】 【6.1】关于正则表达式正则表达式是对字符串操作的一种逻辑公式,用定义好的特定字符和这些特定字符的组合组成一个规则字符串,这个规则字符串原来表达对字符串的一种过滤逻辑,从而实现字符串的检索、替换、匹配验证等。Python 的 re 库提供了整个正则表达式的实现,包含五种方法:match、search、findall、sub、compile常用的匹配规则: 模式 描述 \\w 匹配字母、数字及下划线 \\W 匹配不是字母、数字及下划线的字符 \\s 匹配任意空白字符,等价于 [\\t\\n\\r\\f] \\S 匹配任意非空字符 \\d 匹配任意数字,等价于 [0-9] \\D 匹配任意非数字的字符 \\A 匹配字符串开头 \\z 匹配字符串结尾,如果存在换行,同时还会匹配换行符 \\Z 匹配字符串结尾,如果存在换行,只匹配到换行前的结束字符串 \\G 匹配最后匹配完成的位置 \\n 匹配一个换行符 \\t 匹配一个制表符 ^ 匹配一行字符串的开头 $ 匹配一行字符串的结尾 . 匹配任意字符,除了换行符,当 re.DOTALL 标记被指定时,则可以匹配包括换行符的任意字符 […] 用来表示一组字符,单独列出,比如 [amk] 匹配 a、m 或 k [^…] 不在 [] 中的字符,比如 匹配除了 a、b、c 之外的字符 * 匹配 0 个或多个表达式 + 匹配 1 个或多个表达式 ? 匹配 0 个或 1 个前面的正则表达式定义的片段,非贪婪方式 {n} 精确匹配 n 个前面的表达式 {n, m} 匹配 n 到 m 次由前面正则表达式定义的片段,贪婪方式 a\\ b 匹配 a 或 b ( ) 匹配括号内的表达式,也表示一个组 【6.2】re.match 方法match() 方法会尝试从字符串的起始位置匹配正则表达式,如果匹配,就返回匹配成功的结果;如果不匹配,就返回 None,在 match() 方法中,第一个参数传入正则表达式,第二个参数传入要匹配的字符串。12345678import recontent = 'This is a Demo_123 4567_I Love China'print(len(content))result = re.match('^This\\s\\w\\w\\s\\w\\s\\w{5}\\d{3}\\s\\w{6}', content)print(result)print(result.group())print(result.span()) 输出结果:123436<_sre.SRE_Match object; span=(0, 25), match='This is a Demo_123 4567_I'>This is a Demo_123 4567_I(0, 25) 打印 result 结果是 SRE_Match 对象,表明匹配成功。SRE_Match 对象有两种方法:group() 方法可以输出匹配到的内容;span() 方法可以输出匹配的范围。 【6.2.1】提取内容使用括号将想提取的子字符串括起来。括号实际上标记了一个子表达式的开始和结束位置,被标记的每个子表达式会依次对应每一个分组,调用 group() 方法传入分组的索引即可获取提取的结果。12345678import recontent = 'This is a Demo_123 4567_I Love China'result = re.match('^This\\s\\w\\w\\s\\w\\s(\\w{5})\\d{3}\\s\\w{6}', content)print(result)print(result.group())print(result.group(1))print(result.span()) 输出结果:1234<_sre.SRE_Match object; span=(0, 25), match='This is a Demo_123 4567_I'>This is a Demo_123 4567_IDemo_(0, 25) 【6.2.2】通用匹配如果每个字符都用都用一个符号来匹配的话就显得比较麻烦,可以用 .*来匹配,. 可以匹配除换行符外的任意字符,* 代表匹配前面的字符无限次。1234567import recontent = 'This is a Demo_123 4567_I Love China'result = re.match('^This.*China$', content)print(result)print(result.group())print(result.span()) 输出结果:123<_sre.SRE_Match object; span=(0, 36), match='This is a Demo_123 4567_I Love China'>This is a Demo_123 4567_I Love China(0, 36) 【6.2.3】贪婪匹配12345678import recontent = 'This is a Demo_1234567_I Love China'result = re.match('^This.*(\\d+).*China$', content)print(result)print(result.group())print(result.group(1))print(result.span()) 输出结果:1234<_sre.SRE_Match object; span=(0, 35), match='This is a Demo_1234567_I Love China'>This is a Demo_1234567_I Love China7(0, 35) .* 为贪婪匹配,会匹配尽可能多的字符,所以 \\d+ 只会匹配到最后一个数字,而不是所有的数字 【6.2.4】非贪婪匹配12345678import recontent = 'This is a Demo_1234567_I Love China'result = re.match('^This.*?(\\d+).*China$', content)print(result)print(result.group())print(result.group(1))print(result.span()) 输出结果:1234<_sre.SRE_Match object; span=(0, 35), match='This is a Demo_1234567_I Love China'>This is a Demo_1234567_I Love China1234567(0, 35) .*? 为非贪婪匹配,会匹配尽可能少的字符,所以 \\d+ 会匹配到所有的数字 【6.2.5】转义匹配当遇到用于正则匹配模式的特殊字符时,在前面加反斜线转义一下即可。例如 . 可以用 \\. 来匹配:123456import recontent = '(博客)www.itrhx.com'result = re.match('\\(博客\\)www\\.itrhx\\.com', content)print(result)print(result.group()) 输出结果:12<_sre.SRE_Match object; span=(0, 17), match='(博客)www.itrhx.com'>(博客)www.itrhx.com 【6.2.6】修饰符修饰符用来解决换行、大小写等问题,较为常用的有 re.S 和 re.I。 修饰符 描述 re.S 使 . 匹配包括换行在内的所有字符 re.I 使匹配对大小写不敏感 re.L 做本地化识别(locale-aware)匹配 re.M 多行匹配,影响 ^ 和 $ re.U 根据 Unicode 字符集解析字符。这个标志影响 \\w、\\W、\\b 和 \\B re.X 该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解 示例:123456789import recontent = '''This is a Demo_1234567 _I Love China'''result = re.match('^This.*?(\\d+).*China$', content)print(result)print(result.group())print(result.group(1))print(result.span()) 示例中 content 字段进行了换行处理,如果没有修饰符,就会报错:12345Traceback (most recent call last):None File \"F:/PycharmProjects/Python3爬虫/test.py\", line 7, in <module> print(result.group())AttributeError: 'NoneType' object has no attribute 'group' 添加 re.S 修饰符后即可匹配成功:123456789import recontent = '''This is a Demo_1234567 _I Love China'''result = re.match('^This.*?(\\d+).*China$', content, re.S)print(result)print(result.group())print(result.group(1))print(result.span()) 输出结果:12345<_sre.SRE_Match object; span=(0, 46), match='This is a Demo_1234567\\n _I Love China'>This is a Demo_1234567 _I Love China1234567(0, 46) 【6.3】re.search 方法match() 方法只能从字符串的开头开始匹配,一旦开头不匹配,那么整个匹配就失败了,match() 方法更适合用来检测某个字符串是否符合某个正则表达式的规则,而 search() 方法则会扫描整个字符串并返回第一个成功的匹配123456import recontent = 'This is a Demo_1234567_I Love China'result = re.search('a.*?(\\d{5})', content)print(result)print(result.group(1)) 输出结果:12<_sre.SRE_Match object; span=(8, 20), match='a Demo_12345'>12345 【6.4】re.findall 方法search() 方法则会扫描整个字符串,但是返回的是第一个成功的匹配,而 findall() 方法将会返回所有成功的匹配12345678910111213141516171819202122232425262728import rehtml = '''<div id=\"songs-list\"> <h2 class=\"title\"> 民谣 </h2> <p class=\"introduction\"> 民谣歌曲列表 </p> <ul id=\"list\" class=\"list-group\"> <li data-view=\"2\"> 七里香 </li> <li data-view=\"7\"> <a href=\"/2.mp3\" singer=\"赵雷\"> 理想 </a> </li> <li data-view=\"4\" class=\"active\"> <a href=\"/3.mp3\" singer=\"许巍\"> 像风一样自由 </a> </li> <li data-view=\"6\"><a href=\"/4.mp3\" singer=\"安与骑兵\"> 红山果 </a></li> <li data-view=\"5\"><a href=\"/5.mp3\" singer=\"薛之谦\"> 意外 </a></li> <li data-view=\"5\"> <a href=\"/6.mp3\" singer=\"马頔\"> 但南山南 </a> </li> </ul> </div>'''results = re.findall('<li.*?href=\"(.*?)\".*?singer=\"(.*?)\">(.*?)</a>', html, re.S)print(results)print(type(results))for result in results: print(result) print(result[0], result[1], result[2]) 输出结果:123456789101112[('/2.mp3', '赵雷', ' 理想 '), ('/3.mp3', '许巍', ' 像风一样自由 '), ('/4.mp3', '安与骑兵', ' 红山果 '), ('/5.mp3', '薛之谦', ' 意外 '), ('/6.mp3', '马頔', ' 但南山南 ')]<class 'list'>('/2.mp3', '赵雷', ' 理想 ')/2.mp3 赵雷 理想 ('/3.mp3', '许巍', ' 像风一样自由 ')/3.mp3 许巍 像风一样自由 ('/4.mp3', '安与骑兵', ' 红山果 ')/4.mp3 安与骑兵 红山果 ('/5.mp3', '薛之谦', ' 意外 ')/5.mp3 薛之谦 意外 ('/6.mp3', '马頔', ' 但南山南 ')/6.mp3 马頔 但南山南 【6.5】re.sub 方法与字符串的 replace() 方法类似,sub() 方法可以对文本进行修改,sub() 方法第一个参数为匹配对象,第二个参数为替换成的字符串,如果要去掉匹配对象的话,可以赋值为空,第三个参数为原来的字符串12345import recontent = '87dsf4as2w4jh1k4kdl4'result = re.sub('\\d+', '', content)print(result) 输出结果:1dsfaswjhkkdl 【6.5】re.compile() 方法compile() 方法可以将正则字符串编译成正则表达式对象,以便在后面的匹配中复用123456789101112import recontent1 = '北京时间:2019-08-24 18:30'content2 = '伦敦时间:2019-08-24 11:30'content3 = '巴黎时间:2019-08-24 12:30'content4 = '外星时间:9019-99-66 50:30'pattern = re.compile('\\d{2}:\\d{2}')result1 = re.sub(pattern, '', content1)result2 = re.sub(pattern, '', content2)result3 = re.sub(pattern, '', content3)result4 = re.sub(pattern, '', content4)print(result1, result2, result3, result4) 利用 compile() 方法将正则表达式编译成一个正则表达式对象,以便复用,然后用 sub() 方法去掉具体时间输出结果:1北京时间:2019-08-24 伦敦时间:2019-08-24 巴黎时间:2019-08-24 外星时间:9019-99-66","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫学习","slug":"Python3-学习笔记/爬虫学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫学习/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"正则表达式","slug":"正则表达式","permalink":"https://www.itrhx.com/tags/正则表达式/"}]},{"title":"Python3 爬虫学习笔记 C05","slug":"A35-Python3-spider-C05","date":"2019-08-23T12:13:55.085Z","updated":"2019-09-24T12:40:15.920Z","comments":true,"path":"2019/08/23/A35-Python3-spider-C05/","link":"","permalink":"https://www.itrhx.com/2019/08/23/A35-Python3-spider-C05/","excerpt":"Python3 爬虫学习笔记第五章 —— 【Selenium + 无界面浏览器】","text":"Python3 爬虫学习笔记第五章 —— 【Selenium + 无界面浏览器】 【5.1】关于无界面浏览器无界面(headless)浏览器,会把网站加载到内存并执行页面上的 JavaScript,因为不会展示图形界面,所以运行起来比完整的浏览器更高效。Selenium 搭配无界面浏览器使用,被称为爬虫利器,常用的无界面浏览器有:PhantomJS、Headless Chrome、Headless Firefox,其中,18年3月,PhantomJS 的作者在 GitHub 上宣布暂停开发 PhantomJS,现在使用 PhantomJS 会出现警告:UserWarning: Selenium support for PhantomJS has been deprecated, please use headless versions of Chrome or Firefox instead,所以推荐使用谷歌或者火狐的无界面浏览器 【5.2】PhantomJS下载 PhantomJS:https://phantomjs.org/download.htmlpath 为 PhantomJS 路径,如果系统配置了环境变量,就不用手动指定 executable_path 参数1234567from selenium import webdriverpath = r'F:\\PycharmProjects\\Python3爬虫\\phantomjs-2.1.1\\bin\\phantomjs.exe'driver = webdriver.PhantomJS(executable_path=path)driver.get(\"https://www.itrhx.com\")print(driver.page_source)driver.close() 【5.3】Headless Chrome下载 Chromedriver:http://chromedriver.storage.googleapis.com/index.html需要本地有 Chrome 浏览器,path 为 Headless Chrome 路径,如果系统配置了环境变量,就不用手动指定 executable_path 参数1234567891011from selenium import webdriverfrom selenium.webdriver.chrome.options import Optionschrome_options = Options()chrome_options.add_argument('--headless')chrome_options.add_argument('--disable-gpu')path = 'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe'driver = webdriver.Chrome(executable_path=path, chrome_options=chrome_options)driver.get(\"https://www.itrhx.com\")print(driver.page_source)driver.close() 【5.4】Headless Firefox下载 geckodriver:https://github.com/mozilla/geckodriver/releases/需要本地有 Firefox 浏览器,path 为 Headless Firefox 路径,如果系统配置了环境变量,就不用手动指定 executable_path 参数12345678910from selenium.webdriver import Firefoxfrom selenium.webdriver.firefox.options import Optionsoptions = Options()options.add_argument('-headless')path = 'F:\\PycharmProjects\\Python3爬虫\\geckodriver.exe'driver = Firefox(executable_path=path, firefox_options=options)driver.get(\"https://www.itrhx.com\")print(driver.page_source)driver.close()","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫学习","slug":"Python3-学习笔记/爬虫学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫学习/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"Selenium","slug":"Selenium","permalink":"https://www.itrhx.com/tags/Selenium/"},{"name":"无界面浏览器","slug":"无界面浏览器","permalink":"https://www.itrhx.com/tags/无界面浏览器/"}]},{"title":"常见 User-Agent 大全","slug":"A34-UserAgent","date":"2019-08-23T01:28:22.624Z","updated":"2019-09-24T12:47:21.096Z","comments":true,"path":"2019/08/23/A34-UserAgent/","link":"","permalink":"https://www.itrhx.com/2019/08/23/A34-UserAgent/","excerpt":"","text":"User Agent 中文名为用户代理,简称 UA,是一个特殊字符串头,使得服务器能够识别客户使用的操作系统及版本、CPU 类型、浏览器及版本、浏览器渲染引擎、浏览器语言、浏览器插件等。Python 爬虫通过伪装 UA 可以绕过某些检测。 以下为搜集的常见的各浏览器的 User-Agent,其中: 安卓操作系统:Android 7.1.1;OPPO R9sk Build/NMF26F PC操作系统:Windows 10 64位 10.0.18362.10000 其他操作系统:iOS、Backerry、WebOS、Symbian、Windows Phone 相关链接: 手机User-Agent大全:http://www.fynas.com/ua User-Agent在线检测:http://www.user-agent.cn/ 常用User-Agent大全:http://www.jsons.cn/useragent/ Windows10 Windows10 / Chrome 75.0.3770.142Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36 Windows10 / Firefox 69.0b15Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:69.0) Gecko/20100101 Firefox/69.0 Windows10 / Opera 63.0.3368.43Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36 OPR/63.0.3368.43 Windows10 / Edge 44.18362.1.0User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.18362 Windows10 / IE 11.10000.18362.0User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; LCTE; rv:11.0) like Gecko Windows10 x64 / Safari 5.1.4(7534.54.16)Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/534.54.16 (KHTML, like Gecko) Version/5.1.4 Safari/534.54.16 Windows10 / QQ浏览器 10.5(3739)Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3722.400 QQBrowser/10.5.3739.400 Windows10 / 360安全浏览器 10.0.1977.0Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36 QIHU 360SE Windows10 / 360极速浏览器 11.0.2179.0Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36 QIHU 360EE Windows10 / UC浏览器 6.2.3964.2Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 UBrowser/6.2.3964.2 Safari/537.36 Windows10 / 搜狗浏览器 8.5.10.31270Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 SE 2.X MetaSr 1.0 Windows10 / 猎豹浏览器 6.5.115.19331.8001Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36 LBBROWSER Windows10 / 傲游浏览器 5.2.7.5000Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36 Windows10 / 2345加速浏览器 10.1.0.19399Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3947.100 Safari/537.36 Android Android / Chrome 76.0.3809.111Mozilla/5.0 (Linux; Android 7.1.1; OPPO R9sk) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.111 Mobile Safari/537.36 Android / Firefox 68.0.2Mozilla/5.0 (Android 7.1.1; Mobile; rv:68.0) Gecko/68.0 Firefox/68.0 Android / Opera 53.0.2569.141117Mozilla/5.0 (Linux; Android 7.1.1; OPPO R9sk Build/NMF26F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Mobile Safari/537.36 OPR/53.0.2569.141117 Android / Edge 42.0.2.3819Mozilla/5.0 (Linux; Android 7.1.1; OPPO R9sk) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.90 Mobile Safari/537.36 EdgA/42.0.2.3819 Android / QQ浏览器 9.6.1.5190Mozilla/5.0 (Linux; U; Android 7.1.1; zh-cn; OPPO R9sk Build/NMF26F) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.126 MQQBrowser/9.6 Mobile Safari/537.36 Android / OPPO浏览器 10.5.1.2_2c91537Mozilla/5.0 (Linux; U; Android 7.1.1; zh-cn; OPPO R9sk Build/NMF26F) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.80 Mobile Safari/537.36 OppoBrowser/10.5.1.2 Android / 360浏览器 8.2.0.162Mozilla/5.0 (Linux; Android 7.1.1; OPPO R9sk Build/NMF26F; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/62.0.3202.97 Mobile Safari/537.36 Android / 360极速浏览器 1.0.100.1078Mozilla/5.0 (Linux; Android 7.1.1; OPPO R9sk Build/NMF26F) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.80 Mobile Safari/537.36 360 Alitephone Browser (1.5.0.90/1.0.100.1078) mso_sdk(1.0.0) Android / UC浏览器 12.6.0.1040Mozilla/5.0 (Linux; U; Android 7.1.1; zh-CN; OPPO R9sk Build/NMF26F) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/12.6.0.1040 Mobile Safari/537.36 Android / 猎豹浏览器 5.12.3Mozilla/5.0 (Linux; Android 7.1.1; OPPO R9sk Build/NMF26F; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.80 Mobile Safari/537.36 LieBaoFast/5.12.3 Android / 百度浏览器 7.19Mozilla/5.0 (Linux; Android 7.1.1; OPPO R9sk Build/NMF26F; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/48.0.2564.116 Mobile Safari/537.36 T7/9.1 baidubrowser/7.19.13.0 (Baidu; P1 7.1.1) Android / 搜狗浏览器 5.22.8.71677Mozilla/5.0 (Linux; Android 7.1.1; OPPO R9sk Build/NMF26F; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/68.0.3440.106 Mobile Safari/537.36 AWP/2.0 SogouMSE,SogouMobileBrowser/5.22.8 Android / 2345浏览器 11.0.1Mozilla/5.0 (Linux; Android 7.1.1; OPPO R9sk Build/NMF26F; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.80 Mobile Safari/537.36 Mb2345Browser/11.0.1 其他 iPhone3Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us) AppleWebKit/420.1 (KHTML, like Gecko) Version/3.0 Mobile/1A542a Safari/419.3 iPhone4Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_0 like Mac OS X; en-us) AppleWebKit/532.9 (KHTML, like Gecko) Version/4.0.5 Mobile/8A293 Safari/6531.22.7 iPhone6sMozilla/5.0 (iPhone 6s; CPU iPhone OS 11_4_1 like Mac OS X) AppleWebKit/604.3.5 (KHTML, like Gecko) Version/11.0 MQQBrowser/8.3.0 Mobile/15B87 Safari/604.1 MttCustomUA/2 QBWebViewType/1 WKType/1 iPadMozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B334b Safari/531.21.10 iPodMozilla/5.0 (iPod; U; CPU iPhone OS 4_3_3 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5 BlackBerryMozilla/5.0 (BlackBerry; U; BlackBerry 9800; en) AppleWebKit/534.1+ (KHTML, like Gecko) Version/6.0.0.337 Mobile Safari/534.1+ WebOS HP TouchpadMozilla/5.0 (hp-tablet; Linux; hpwOS/3.0.0; U; en-US) AppleWebKit/534.6 (KHTML, like Gecko) wOSBrowser/233.70 Safari/534.6 TouchPad/1.0 Nokia N97Mozilla/5.0 (SymbianOS/9.4; Series60/5.0 NokiaN97-1/20.0.019; Profile/MIDP-2.1 Configuration/CLDC-1.1) AppleWebKit/525 (KHTML, like Gecko) BrowserNG/7.1.18124 Windows Phone MangoMozilla/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident/5.0; IEMobile/9.0; HTC; Titan)","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"学习经验","slug":"Python3-学习笔记/学习经验","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/学习经验/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"User-Agent","slug":"User-Agent","permalink":"https://www.itrhx.com/tags/User-Agent/"}]},{"title":"Selenium 显式等待条件及其含义","slug":"A33-selenium","date":"2019-08-23T01:28:22.478Z","updated":"2019-09-24T12:47:18.475Z","comments":true,"path":"2019/08/23/A33-selenium/","link":"","permalink":"https://www.itrhx.com/2019/08/23/A33-selenium/","excerpt":"","text":"等待条件 含义 title_is 标题是某内容 title_contains 标题包含某内容 presence_of_element_located 节点加载出,传入定位元组,如 (By.ID, ‘p’) visibility_of_element_located 节点可见,传入定位元组 visibility_of 可见,传入节点对象 presence_of_all_elements_located 所有节点加载出 text_to_be_present_in_element 某个节点文本包含某文字 text_to_be_present_in_element_value 某个节点值包含某文字 frame_to_be_available_and_switch_to_it frame 加载并切换 invisibility_of_element_located 节点不可见 element_to_be_clickable 节点可点击 staleness_of 判断一个节点是否仍在 DOM,可判断页面是否已经刷新 element_to_be_selected 节点可选择,传节点对象 element_located_to_be_selected 节点可选择,传入定位元组 element_selection_state_to_be 传入节点对象以及状态,相等返回 True,否则返回 False element_located_selection_state_to_be 传入定位元组以及状态,相等返回 True,否则返回 False alert_is_present 是否出现 Alert 更多等待条件极其用法介绍:https://selenium-python.readthedocs.io/api.html#module-selenium.webdriver.support.expected_conditions Selenium 的使用:https://www.itrhx.com/2019/08/22/A32-Python3-spider-C04/","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"学习经验","slug":"Python3-学习笔记/学习经验","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/学习经验/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"Selenium","slug":"Selenium","permalink":"https://www.itrhx.com/tags/Selenium/"}]},{"title":"Python3 爬虫学习笔记 C04","slug":"A32-Python3-spider-C04","date":"2019-08-23T01:28:22.327Z","updated":"2019-09-24T12:43:57.196Z","comments":true,"path":"2019/08/23/A32-Python3-spider-C04/","link":"","permalink":"https://www.itrhx.com/2019/08/23/A32-Python3-spider-C04/","excerpt":"Python3 爬虫学习笔记第四章 —— 【自动化测试工具 Selenium】","text":"Python3 爬虫学习笔记第四章 —— 【自动化测试工具 Selenium】 Selenium 是一个用于 Web 应用程序测试的工具。Selenium 测试直接运行在浏览器中,就像真正的用户在操作一样。支持的浏览器包括IE(7, 8, 9, 10, 11),Mozilla Firefox,Safari,Google Chrome,Opera等。利用它可以驱动浏览器执行特定的动作,如点击、下拉等操作,同时还可以获取浏览器当前呈现的页面的源代码,做到可见即可爬。对于一些 JavaScript 动态渲染的页面来说,此种抓取方式非常有效。本文重点以 Selenium 使用谷歌浏览器的 Webdriver 为例。 【4.1】下载驱动使用 Selenium 操作不同浏览器,需要不同浏览器相应的驱动支持: 浏览器 驱动名称 下载地址 备注 谷歌浏览器 chromedriver 点击进入下载页面 需要根据自己浏览器的版本下载不同版本的驱动 火狐浏览器 geckodriver 点击进入下载页面 需要根据自己的操作系统下载对应的驱动 IE IEDriverServer 点击进入下载页面 根据自己 selenium 版本和系统版本下载对应版本的驱动, selenium 版本可以在cmd中输入pip show selenium查看 【4.2】声明浏览器对象不同浏览器的对象声明方法:1234567from selenium import webdriverbrowser = webdriver.Chrome() # 谷歌浏览器browser = webdriver.Firefox() # 火狐浏览器browser = webdriver.Edge() # Edgebrowser = webdriver.PhantomJS() # PhantomJS无界面浏览器browser = webdriver.Safari() # Safari浏览器 【4.3】访问页面1234567from selenium import webdriverpath = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe'browser = webdriver.Chrome(executable_path=path)browser.get('https://www.itrhx.com')print(browser.page_source)browser.close() 运行代码就会自动打开谷歌浏览器,实现了用 get() 方法访问 www.itrhx.com ,path 里面的内容是谷歌浏览器驱动的目录, r 表示不转义,使用真实字符。print(browser.page_source) 表示打印页面源代码 【4.4】启动参数Chrome Options 是一个 Chrome 的参数对象,在此对象中使用 add_argument() 方法可以添加启动参数,添加完毕后可以在初始化 Webdriver 对象时将此 Options 对象传入,则可以实现以特定参数启动Chrome。示例:123456789101112from selenium import webdriverfrom selenium.webdriver.chrome.options import Optionspath = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe'# 实例化一个启动参数对象chrome_options = Options()# 添加启动参数chrome_options.add_argument('--window-size=1366,768')# 将参数对象传入Chrome,则启动了一个设置了窗口大小的Chromebrowser = webdriver.Chrome(executable_path=path, chrome_options=chrome_options)browser.get('http://www.itrhx.com') 这样就启动了一个1366x768分辨率的浏览器常见的启动参数: 启动参数 作用 –user-agent=”” 设置请求头的 User-Agent –window-size=xxx, xxx 设置浏览器分辨率 –headless 无界面运行 –start-maximized 最大化运行 –incognito 隐身模式 –disable-javascript 禁用javascript –disable-infobars 禁用“浏览器正在被自动化程序控制”的提示 所有的启动参数:https://peter.sh/experiments/chromium-command-line-switches/ 【4.5】查找节点Selenium 可以驱动浏览器完成各种操作,比如填充表单、模拟点击等。要完成这些操作,实现要知道在哪里点击,哪里填充,这就是 Selenium 节点查找 【4.5.1】查找单个节点所有获取单个节点的方法: find_element_by_id 【通过元素的 id 来选择】例:<div id='bdy-inner'>test</div>,查找:driver.find_element_by_id('bdy-inner') find_element_by_name 【通过元素的 name 来选择】例:<input name="username" type="text" />,查找:driver.find_element_by_name('password') find_element_by_xpath 【通过 xpath 选择】例:<form id="loginForm">,查找:driver.find_element_by_xpath("//form[@id='loginForm']") find_element_by_link_text 【通过链接地址选择】例:<a href="continue.html">continue</a>,查询:driver.find_element_by_link_text('continue') find_element_by_partial_link_text 【通过链接的部分地址选择】例:<a href="continue.html">continue</a>,查询:driver.find_element_by_link_text('cont') find_element_by_tag_name 【通过元素的名称选择】例:<h1>welcome<h1>,查询:driver.find_element_by_tag_name('h1') find_element_by_class_name 【通过元素的 class 选择】例:<p class="content">welcome to TRHX'S BLOG!</p>,查询:driver.find_element_by_class_name('content') find_element_by_css_selector 【通过元素的 class 选择】例:<div class='bdy-inner'>test</div>,查询:driver.find_element_by_css_selector('div.bdy-inner') find_element() 【通用方法,需要传递两个参数:查找方式 By 和值】例:driver.find_element_by_id('inner') 等价于 find_element(By.ID, inner),使用时需要from selenium.webdriver.common.by import By 示例:12345678from selenium import webdriverpath = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe'browser = webdriver.Chrome(executable_path=path)browser.get('https://www.itrhx.com')blog_title = browser.find_elements_by_class_name(('title'))print(blog_title[0].text)browser.close() 输出结果:1TRHX'S BLOG 【4.5.2】查找多个节点所有获取多个节点的方法:(与查找单个节点的区别是 element 多加了个 s) find_elements_by_id find_elements_by_name find_elements_by_xpath find_elements_by_link_text find_elements_by_partial_link_text find_elements_by_tag_name find_elements_by_class_name find_elements_by_css_selector find_elements() 示例:123456789from selenium import webdriverfrom selenium.webdriver.common.by import Bypath = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe'browser = webdriver.Chrome(executable_path=path)browser.get('https://www.itrhx.com')article_title = browser.find_elements(By.XPATH, \"//h2[@class='title']\")print(article_title)browser.close() 【4.6】节点交互Selenium 可以驱动浏览器来执行一些操作,也就是说可以让浏览器模拟执行一些动作。称为节点交互,比较常见的用法有: send_keys:模拟按键输入 clear:清除元素的内容 click:单击元素 submit:提交表单 示例:123456789from selenium import webdriverfrom selenium.webdriver.common.keys import Keyspath = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe'browser = webdriver.Chrome(executable_path=path)browser.get('https://www.itrhx.com')search = browser.find_element_by_xpath('//div[@class=\"cover-wrapper\"]/cover/div/form/input')search.send_keys(\"Python\")search.send_keys(Keys.ENTER) 此处模拟了键盘,需要导入键盘类 Keys(),send_keys(Keys.ENTER)表示模拟回车键,程序首先打开 www.itrhx.com ,也就是我的博客,然后通过 xpath 找到搜索框,输入 Python 并回车,等待结果显示出来更多节点交互动作:https://selenium-python.readthedocs.io/api.html#module-selenium.webdriver.remote.webelement 【4.7】动作链Selenium 还有另外一些操作,它们没有特定的执行对象,比如鼠标拖曳、键盘按键等,这些动作用另一种方式来执行,那就是动作链。以一个拖曳实例为例:http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable12345678910111213from selenium import webdriverfrom selenium.webdriver import ActionChainspath = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe'browser = webdriver.Chrome(executable_path=path)url = 'http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable'browser.get(url)browser.switch_to.frame('iframeResult')source = browser.find_element_by_css_selector('#draggable')target = browser.find_element_by_css_selector('#droppable')actions = ActionChains(browser)actions.drag_and_drop(source, target)actions.perform() 依次选中要拖曳的节点和拖曳到的目标节点,接着声明 ActionChains 对象并将其赋值为 actions 变量,然后通过调用 actions 变量的 drag_and_drop() 方法,再调用 perform() 方法执行动作,此时就完成了拖曳操作,更多动作链操作:https://selenium-python.readthedocs.io/api.html#module-selenium.webdriver.common.action_chains 【4.8】执行 JavaScriptSelenium API 并没有提供执行 JavaScript 的方法,但是实际上是可以实现的。比如,下拉进度条,它可以直接模拟运行 JavaScript,此时使用 execute_script() 方法即可实现示例:1234567from selenium import webdriverpath = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe'browser = webdriver.Chrome(executable_path=path)browser.get('https://www.itrhx.com')browser.execute_script('window.scrollTo(0, document.body.scrollHeight)')browser.execute_script('alert(\"已到达最底端!\")') 以上代码实现了利用 execute_script() 方法将进度条下拉到最底部,然后弹出 alert 提示框。 【4.9】禁用加载使用Selenium 时,限制图片和 Javascript 执行,从而提高网页加载速度。123456789101112131415from selenium import webdriverpath = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe'options = webdriver.ChromeOptions()prefs = { 'profile.default_content_setting_values': { 'images': 2, 'notifications' : 2, # 禁用弹窗 'javascript': 2 # 2即为禁用的意思 }}options.add_experimental_option('prefs', prefs)browser = webdriver.Chrome(executable_path=path, chrome_options=options)browser.get('http://www.itrhx.com') 【4.10】获取节点信息通过 page_source 属性可以获取网页的源代码,然后可以使用解析库(如正则表达式、Beautiful Soup等)来提取相关信息,Selenium 已经提供了选择节点的方法,返回的是 WebElement 类型,它也有相关的方法和属性来直接提取节点信息,如属性、文本等。就不需要再次使用解析库来提取信息了 【4.10.1】获取属性使用 get_attribute() 方法来获取节点的属性:123456789from selenium import webdriverpath = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe'browser = webdriver.Chrome(executable_path=path)url = 'http://www.itrhx.com'browser.get(url)meta = browser.find_element_by_id('header-meta')print(meta)print(meta.get_attribute('class')) 输出结果:12<selenium.webdriver.remote.webelement.WebElement (session=\"d03cdaa497441d2e2a5161139b4a7ea5\", element=\"83f8fff9-60d7-4e9a-ade3-a8e97c9f0844\")>meta 【4.10.2】获取文本值每个 WebElement 节点都有 text 属性,直接调用这个属性就可以得到节点内部的文本信息,相当于 Beautiful Soup 的 get_text() 方法、pyquery 的 text() 方法示例:12345678from selenium import webdriverpath = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe'browser = webdriver.Chrome(executable_path=path)url = 'http://www.itrhx.com'browser.get(url)footer_info = browser.find_element_by_id('footer')print(footer_info.text) 输出结果:123Copyright 2018-2019 TRHX'BLOG | 鄂ICP备19003281号-4 | 本站已勉强存活了 376 天 20 小时 57 分 52 秒 | 站点地图 | 站长统计PoweredHexo HostedGitHub DNRAliyun CDNjsDelivr ThemeMaterial X BY-NC-SA 4.0 Link996.ICU UV4898 PV22066 WordCount54.9k 【4.10.3】获取 ID、位置、标签名、大小其他属性,比如 id 属性可以获取节点 id,location 属性可以获取该节点在页面中的相对位置,tag_name 属性可以获取标签名称,size 属性可以获取节点的大小等示例:1234567891011from selenium import webdriverpath = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe'browser = webdriver.Chrome(executable_path=path)url = 'http://www.itrhx.com'browser.get(url)readmore = browser.find_element_by_class_name('readmore')print(readmore.id)print(readmore.location)print(readmore.tag_name)print(readmore.size) 输出结果:12347df561d3-7ea4-4b90-96aa-64044060bb47{'x': 50, 'y': 1063}div{'height': 39, 'width': 465} 【4.11】延时等待在 Selenium 中,get() 方法会在网页框架加载结束后结束执行,某些页面有额外的 Ajax 请求,若此时立即获取 page_source,可能并不是浏览器完全加载完成的页面,这里需要延时等待一定时间,确保节点已经加载出来 【4.11.1】隐式等待当查找节点的时候,节点并没有立即出现,隐式等待将等待一段时间再查找该节点,使用 implicitly_wait() 方法可以实现隐式等待12345678from selenium import webdriverpath = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe'browser = webdriver.Chrome(executable_path=path)browser.implicitly_wait(10)browser.get('https://www.itrhx.com')readmore = browser.find_element_by_class_name('readmore')print(readmore) 【4.11.2】显式等待指定要查找的节点,然后指定一个最长等待时间。如果在规定时间内加载出来了这个节点,就立即返回查找的节点,果到了规定时间依然没有加载出该节点,则抛出超时异常123456789101112from selenium import webdriverfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECpath = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe'browser = webdriver.Chrome(executable_path=path)browser.implicitly_wait(10)browser.get('https://www.itrhx.com')wait = WebDriverWait(browser, 10)footer_info = wait.until(EC.presence_of_element_located((By.ID, 'footer')))print(footer_info) 引入 WebDriverWait 对象,指定最长等待时间,调用它的 until() 方法,传入要等待条件 expected_conditions。比如,这里传入了 presence_of_element_located 这个条件,代表节点出现的意思,其参数是节点的定位元组,也就是 ID 为 footer 的节点。 这样可以做到的效果就是,在 10 秒内如果 ID 为 footer 的节点成功加载出来,就返回该节点;如果超过 10 秒还没有加载出来,就抛出异常。 加载成功时输出结果:1<selenium.webdriver.remote.webelement.WebElement (session=\"4ca7015891fded627ab680d9462e9361\", element=\"3a80235c-9824-420b-b827-662638422765\")> 加载失败时输出结果:12345TimeoutException Traceback (most recent call last)<ipython-input-4-f3d73973b223> in <module>() 7 browser.get('https://www.itrhx.com') 8 wait = WebDriverWait(browser, 10)----> 9 input = wait.until(EC.presence_of_element_located((By.ID, 'footer'))) 【4.12】Cookies使用 Selenium,可以方便地对 Cookies 进行获取、添加、删除等操作:12345678910from selenium import webdriverpath = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe'browser = webdriver.Chrome(executable_path=path)browser.get('https://www.zhihu.com/explore')print(browser.get_cookies())browser.add_cookie({'name': 'TRHX', 'domain': 'www.zhihu.com', 'value': 'germey'})print(browser.get_cookies())browser.delete_all_cookies()print(browser.get_cookies()) 访问知乎,加载完成后,浏览器已经生成了 Cookies。调用 get_cookies() 方法获取所有的 Cookies。然后再添加一个 Cookie,传入一个字典,有 name、domain 和 value 等内容。接下来,再次获取所有的 Cookies。可以发现,结果就多了这一项新加的 Cookie。最后,调用 delete_all_cookies() 方法删除所有的 Cookies。再重新获取,发现结果就为空了输出结果:123[{'domain': 'zhihu.com', 'expiry': 1661065738.754333, 'httpOnly': False, 'name': 'd_c0', 'path': '/', 'secure': False, 'value': '\"AODi_Lod7g-PTrrXUgXb1N4MkbStCrbNlD4=|1566457741\"'}, {'domain': 'zhihu.com', 'httpOnly': False, 'name': '_xsrf', 'path': '/', 'secure': False, 'value': 'aba68431-9daf-4b62-a67a-023c1a24f0e8'}, {'domain': 'zhihu.com', 'expiry': 1629529738.75427, 'httpOnly': False, 'name': '_zap', 'path': '/', 'secure': False, 'value': 'b6f63cfc-a525-4ae6-a7bf-6384bd1e0548'}, {'domain': 'www.zhihu.com', 'expiry': 1566458637.754178, 'httpOnly': False, 'name': 'tgw_l7_route', 'path': '/', 'secure': False, 'value': '116a747939468d99065d12a386ab1c5f'}][{'domain': 'www.zhihu.com', 'httpOnly': False, 'name': 'TRHX', 'path': '/', 'secure': True, 'value': 'germey'}, {'domain': 'zhihu.com', 'expiry': 1661065738.754333, 'httpOnly': False, 'name': 'd_c0', 'path': '/', 'secure': False, 'value': '\"AODi_Lod7g-PTrrXUgXb1N4MkbStCrbNlD4=|1566457741\"'}, {'domain': 'zhihu.com', 'httpOnly': False, 'name': '_xsrf', 'path': '/', 'secure': False, 'value': 'aba68431-9daf-4b62-a67a-023c1a24f0e8'}, {'domain': 'zhihu.com', 'expiry': 1629529738.75427, 'httpOnly': False, 'name': '_zap', 'path': '/', 'secure': False, 'value': 'b6f63cfc-a525-4ae6-a7bf-6384bd1e0548'}, {'domain': 'www.zhihu.com', 'expiry': 1566458637.754178, 'httpOnly': False, 'name': 'tgw_l7_route', 'path': '/', 'secure': False, 'value': '116a747939468d99065d12a386ab1c5f'}][{'domain': 'zhihu.com', 'expiry': 1644217741.489889, 'httpOnly': False, 'name': '_xsrf', 'path': '/', 'secure': False, 'value': 'WNOjpDbNmz36B4nG1lzSAuPdTyORMX6J'}] 【4.13】前进与后退使用 back() 方法后退,使用 forward() 方法前进,与浏览器的前进后退一样示例:123456789101112from selenium import webdriverimport timepath = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe'browser = webdriver.Chrome(executable_path=path)browser.get('https://www.itrhx.com/')browser.get('https://www.baidu.com/')browser.get('https://www.zhihu.com/')browser.back()time.sleep(1)browser.forward()browser.close() 【4.14】选项卡和浏览器一样,在 Selenium 中也可以新建一个选项卡12345678910111213from selenium import webdriverimport timepath = r'F:\\PycharmProjects\\Python3爬虫\\chromedriver.exe'browser = webdriver.Chrome(executable_path=path)browser.get('https://www.itrhx.com')browser.execute_script('window.open()')print(browser.window_handles)browser.switch_to.window(browser.window_handles[1])browser.get('https://www.baidu.com')time.sleep(1)browser.switch_to.window(browser.window_handles[0])browser.get('https://www.zhihu.com') 首先访问我的博客,然后调用了 execute_script() 方法,传入 window.open() 这个 JavaScript 语句开启一个新的选项卡。再调用 window_handles 属性获取当前开启的所有选项卡,返回的是选项卡的代号列表。调用 switch_to_window() 方法来切换选项卡,其中参数是选项卡的代号。输出的选项卡代号列表:1['CDwindow-C9CADF1ED28CE44970655238552A8DCF', 'CDwindow-538D7F81E467746B7BB2D9D82E2D036E']","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫学习","slug":"Python3-学习笔记/爬虫学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫学习/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"Selenium","slug":"Selenium","permalink":"https://www.itrhx.com/tags/Selenium/"}]},{"title":"Python3 爬虫学习笔记 C03","slug":"A31-Python3-spider-C03","date":"2019-08-23T01:28:22.178Z","updated":"2019-09-24T12:43:59.900Z","comments":true,"path":"2019/08/23/A31-Python3-spider-C03/","link":"","permalink":"https://www.itrhx.com/2019/08/23/A31-Python3-spider-C03/","excerpt":"Python3 爬虫学习笔记第三章 ——【Ajax 数据爬取】","text":"Python3 爬虫学习笔记第三章 ——【Ajax 数据爬取】 【3.1】Ajax 简介Ajax — Asynchronous Javascript And XML(异步 JavaScript 和 XML),是指一种创建交互式网页应用的网页开发技术。可以在不重新加载整个网页的情况下,对网页的某部分进行更新。 【3.2】解析真实地址提取以豆瓣电影动作片排行榜为例,地址为:https://movie.douban.com/typerank?type_name=%E5%8A%A8%E4%BD%9C&type=5&interval_id=100:90&action= ,首先使用常用方法来爬取电影信息:12345678import requestsurl = 'https://movie.douban.com/typerank?type_name=%E5%8A%A8%E4%BD%9C&type=5&interval_id=100:90&action='headers = {\"User-Agent\": \"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 SE 2.X MetaSr 1.0\"}response = requests.get(url, headers=headers)print(response.text) 得到的数据里面我们并没有找到电影相关信息:再次分析页面,发现鼠标下滑的时候,页面不刷新,URL 也不变,但是会加载新数据,那么此处就运用了 Ajax,可以使用抓包工具或者浏览器控制台来捕获 Ajax 接口,获取其真实地址,XHR 是 Ajax 特殊的请求类型,返回的是 json 数据,利用浏览器控制台过滤 XHR,随便点击一条请求,可以看到其 Request URL,也就是真实地址,点击 Preview 就可以看到返回的 json 数据。同样,我们可以使用 Fiddler 抓包软件抓取 Ajax 接口:分析其真实地址为:https://movie.douban.com/j/chart/top_list?type=5&interval_id=100%3A90&action=&start=20&limit=20 ,多下滑几次,只有 start 参数发生了改变,观察变化可知:每一次页面将多出20个电影信息,start 为从第几个电影开始,由此就不难进行数据抓取了 代码:1234567891011121314import requestsurl = 'https://movie.douban.com/j/chart/top_list?type=5&interval_id=100%3A90&action=&'page = int(input('请输入想要第几页的数据:'))data = { 'start': (page - 1)*20, 'limit': '20',}headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36',}response = requests.get(url, params=data, headers=headers)print(response.text) 运行代码即可得到电影排行信息:","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫学习","slug":"Python3-学习笔记/爬虫学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫学习/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"Ajax","slug":"Ajax","permalink":"https://www.itrhx.com/tags/Ajax/"}]},{"title":"Python3 爬虫学习笔记 C02","slug":"A30-Python3-spider-C02","date":"2019-08-23T01:28:22.053Z","updated":"2020-03-14T06:19:49.153Z","comments":true,"path":"2019/08/23/A30-Python3-spider-C02/","link":"","permalink":"https://www.itrhx.com/2019/08/23/A30-Python3-spider-C02/","excerpt":"Python3 爬虫学习笔记第二章 ——【基本库 requests 的使用】","text":"Python3 爬虫学习笔记第二章 ——【基本库 requests 的使用】 【2.1】 requests 简介在 Python 中有两种方式可以发送 HTTP 请求,分别是自带的 urllib 库和第三方的 requests 库 requests 模块需要使用 pip install 命令安装安装,相比 urllib,它的 API 更加人性化,使用 requests 可以让 Cookies、登录验证、代理设置等操作更加简便,官网介绍:http://cn.python-requests.org 【2.2】 requests 基本用法示例:123456789import requestsr = requests.get('https://www.itrhx.com/')print(type(r))print(r.encoding)print(r.status_code)print(r.cookies)print(r.json)print(r.text)print(r.content) 输出结果:12345678910<class 'requests.models.Response'>utf-8200<RequestsCookieJar[]><bound method Response.json of <Response [200]>><!DOCTYPE html><html><head> <meta charset=\"utf-8\"> ...... r.encoding:服务器内容使用的文本编码; r.status_code:响应状态码,200 代表成功,4xx 代表客户端错误,5xx 服务器响应错误; r.cookies:返回 Cookies; r.json:Requests 内置 JSON 解码器; r.text:服务器响应内容,根据响应头部的字符编码自动解码; r.content:字节方式的响应体,自动解码 gzip 和 deflate 编码的响应。 【2.3】 requests 构建 GET 请求 【2.3.1】 基本用法示例:123456789import requestsdata = { 'name': 'TRHX', 'age': '20'}r = requests.get(\"http://httpbin.org/get\", params=data)print('编码后的URL:', r.url)print('字符串方式的响应体:', r.text) 输出结果:123456789101112131415编码后的URL: http://httpbin.org/get?name=TRHX&age=20字符串方式的响应体: { \"args\": { \"age\": \"20\", \"name\": \"TRHX\" }, \"headers\": { \"Accept\": \"*/*\", \"Accept-Encoding\": \"gzip, deflate\", \"Host\": \"httpbin.org\", \"User-Agent\": \"python-requests/2.22.0\" }, \"origin\": \"171.115.102.230, 171.115.102.230\", \"url\": \"https://httpbin.org/get?name=TRHX&age=20\"} 【2.3.2】 二进制数据抓取以抓取 GitHub 站点图标为例:12345import requestsr = requests.get(\"https://github.com/favicon.ico\")with open('favicon.ico', 'wb') as f: f.write(r.content) 该代码将会保存站点图标到本地,其他的,比如音频,视频文件都是由二进制码组成的,皆可使用该方法 【2.3.3】 添加 headersheaders 的作用:部分页面禁止 Python 爬虫对其进行爬取,而添加 headers 就可以模拟成浏览器取访问网站,实现数据的爬取,headers 可以在任意网页 F12 检查控制台里面找到,headers 最重要的是 “User-Agent” 字段 以为例知乎,只有加了 headers 才能正常爬取,否则会返回 400 Bad Request 没有任何数据 123456import requestsheaders = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'}r = requests.get(\"https://www.zhihu.com/explore\", headers=headers)print(r.text) 【2.4】 requests 构建 POST 请求示例:12345import requestsdata = {'name': 'TRHX', 'age': '20'}r = requests.post(\"http://httpbin.org/post\", data=data)print(r.text) 输出结果: 1234567891011121314151617181920{ \"args\": {}, \"data\": \"\", \"files\": {}, \"form\": { \"age\": \"22\", \"name\": \"germey\" }, \"headers\": { \"Accept\": \"*/*\", \"Accept-Encoding\": \"gzip, deflate\", \"Content-Length\": \"18\", \"Content-Type\": \"application/x-www-form-urlencoded\", \"Host\": \"httpbin.org\", \"User-Agent\": \"python-requests/2.22.0\" }, \"json\": null, \"origin\": \"171.115.102.230, 171.115.102.230\", \"url\": \"https://httpbin.org/post\"} 有关 POST 和 GET 两种请求的一些区别: POST 更加安全,不会作为 URL 的一部分,不会被缓存,保存在服务器日志、以及浏览器浏览记录中; POST 发送的数据更大,GET 有 URL 长度限制; POST 可以发送更多的数据类型,GET 只能发送 ASCII 字符; POST 比 GET 慢; POST 查询参数在 WebForms 保存,GET 查询参数在 QueryString 保存; POST 用数据的修改和写入,GET 一般用于搜索排序和筛选之类的操作。 【2.5】 requests 高级用法 【2.5.1】 上传文件示例: 12345import requestsfiles = {'file': open('test.png', 'rb')}r = requests.post('http://httpbin.org/post', files=files)print(r.text) 输出结果: 12345678910111213141516171819{ \"args\": {}, \"data\": \"\", \"files\": { \"file\": \"data:application/octet-stream;base64,iVBOR......\" }, \"form\": {}, \"headers\": { \"Accept\": \"*/*\", \"Accept-Encoding\": \"gzip, deflate\", \"Content-Length\": \"81383\", \"Content-Type\": \"multipart/form-data; boundary=e36a8686cd77c79dc02bfe9d1b010f08\", \"Host\": \"httpbin.org\", \"User-Agent\": \"python-requests/2.22.0\" }, \"json\": null, \"origin\": \"171.115.102.230, 171.115.102.230\", \"url\": \"https://httpbin.org/post\"} 【2.5.2】 使用 Cookies对于需要登录后才能获取数据的网页,可以将账号登录的 Cookies 添加到 headers 来实现网页登录爬取,Cookies 可以抓包获取,代码示例: 123456789import requestsheaders = { 'Cookie': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'Host': 'www.zhihu.com', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36',}r = requests.get('https://www.zhihu.com', headers=headers)print(r.text) 【2.5.3】 会话维持 背景介绍:利用 get() 或者 post() 方法来模拟网页请求,相当于是不同的会话,可以理解为用两个浏览器打开了不同的网页; 运用场景:首先使用 post() 方法登录网页,然后再使用 get() 方法请求某个页面信息,如果不利用会话维持,将无法获取页面数据 维持方法:①两次请求设置一样的 cookies,缺点:繁琐;②使用 Session 对象。 Session 对象使用示例: 123456 import requestss = requests.Session()s.get('http://httpbin.org/cookies/set/number/123456789')r = s.get('http://httpbin.org/cookies')print(r.text) 输出结果成功获取到设置的 cookies: 12345{ \"cookies\": { \"number\": \"123456789\" }} 【2.5.4】 SSL 证书验证 SSL 证书是数字证书的一种,由受信任的数字证书颁发机构 CA 在验证服务器身份后颁发,具有服务器身份验证和数据传输加密功能,网站带有 HTTPS 就表明有 SSL 证书 requests 提供了证书验证的功能。当发送 HTTP 请求的时候,它会检查 SSL 证书,verify 参数可以控制是否检查此证书。如果不加 verify 参数,默认为 True,会自动验证。当一个页面的 SSL 证书没有被官方机构认证时,打开页面就会提示“您的连接不是私密连接”,如果没有设置 verify 参数,将会报以下错误: 1requests.exceptions.SSLError: (\"bad handshake: Error([('SSL routines', 'tls_process_server_certificate', 'certificate verify failed')],)\",) 设置 verify 参数代码示例: 1234import requestsresponse = requests.get('https://www.itrhx.com', verify=False)print(response.text) 【2.5.5】 设置代理为什么要设置代理:某些网页有反爬虫机制,频繁请求网页就会出现验证码等,还有可能直接封掉 IP,导致爬取失败;这种情况下就可以设置 proxies 参数。示例: 12345678import requestsproxies = { 'http': 'http://10.10.1.10:1010', 'https': 'http://10.10.1.10:1020',}requests.get('https://www.itrhx.com', proxies=proxies) 免费代理可在西刺代理找到 【2.5.6】 超时设置与 urllib.request.urlopen() 类似,requests 也可以设置 timeout 参数,请求分为两个阶段:连接和读取 设置连接和读取时间总和: 1234import requestsr = requests.get('https://www.itrhx.com', timeout=1)print(r.status_code) 分别设置连接和读取时间: 1234import requestsr = requests.get('https://www.itrhx.com', timeout=(5, 10))print(r.status_code) 永久等待: 123456import requests# 两种方法实现# r = requests.get('https://www.itrhx.com')r = requests.get('https://www.itrhx.com', timeout=None)print(r.status_code)","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫学习","slug":"Python3-学习笔记/爬虫学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫学习/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"requests","slug":"requests","permalink":"https://www.itrhx.com/tags/requests/"}]},{"title":"Python3 爬虫学习笔记 C01","slug":"A29-Python3-spider-C01","date":"2019-08-23T01:28:21.841Z","updated":"2019-09-24T12:39:54.253Z","comments":true,"path":"2019/08/23/A29-Python3-spider-C01/","link":"","permalink":"https://www.itrhx.com/2019/08/23/A29-Python3-spider-C01/","excerpt":"Python3 爬虫学习笔记第一章 ——【基本库 urllib 的使用】","text":"Python3 爬虫学习笔记第一章 ——【基本库 urllib 的使用】 【1.1】 urllib 简介在 Python 中有两种方式可以发送 HTTP 请求,分别是自带的 urllib 库和第三方的 requests 库 urllib 库:Python 内置的 HTTP 请求库,无需额外安装即可使用;Python 2 中有 urllib 和 urllib2 两个库来实现请求的发送,Python 3 中统一为 urllib。官方文档:https://docs.python.org/3/library/urllib.html urllib 所包含的常用模块: urllib.request:模拟发送请求; urllib.error:异常处理模块,用于捕获异常; urllib.parse:解析、拆分、合并URL; urllib.robotparser:读取网站的 robots.txt 文件,判断哪些内容可以爬取。 urllib.request 所包含的常用方法: urllib.request.urlopen():打开网址URL,这可以是一个字符串或一个 Request对象; urllib.request.Request():在请求的时候传入一些 headers 等信息; urllib.request.urlretrieve():将获取的URL的内容写到文件目录中去。 urllib.error 所包含的两个异常: URLError:继承自 OSError 类,是 error 异常模块的基类,由 request 模块产生的异常都可以通过捕获这个类来处理。 HTTPError:是 URLError 的子类,专门用来处理 HTTP 请求错误,比如认证请求失败等。 urllib.parse 所包含的常用方法: urllib.parse.urlencode():将字典参数序列化为 GET 请求参数; urllib.parse.parse_qs():将 GET 请求参数反序列化转回字典; urllib.parse.parse_qsl():将参数转化为元组组成的列表; urllib.parse.urlparse():对 URL 进行分段(返回6个结果); urllib.parse.urlunparse():对 URL 进行组合(长度必须为6); urllib.parse.urlsplit():对 URL 进行分段(不单独解析params部分,返回5个结果); urllib.parse.urlunsplit():对 URL 进行组合(长度必须为5); urllib.parse.urljoin():对 URL 进行组合(没有长度限制,给定两个参数,自动分析 scheme、netloc 和 path 这 3 个内容并对新链接缺失的部分进行补充,最后返回结果); urllib.parse.quote():将内容转化为 URL 编码格式; urllib.parse.unquote():对 URL 进行解码。 urllib.robotparser 所包含的类: RobotFileParser:根据网站的 robots.txt 文件来判断一个爬取爬虫是否有权限来爬取这个网页 【1.2】 urllib.request 发送请求【1.2.1】 urllib.request.urlopen()【1.2.1.1】 基本使用方法urlopen() 函数的 API:1urllib.request.urlopen(url, data=None, [timeout,]*, cafile=None, capath=None, cadefault=False, context=None) 基本使用:运行以下代码可得到 https://www.itrhx.com/ 的网页源代码:1234import urllib.requestresponse = urllib.request.urlopen('https://www.itrhx.com/')print(response.read().decode('utf-8')) 输出响应对象的类型和属性:1234567import urllib.requestresponse = urllib.request.urlopen('https://www.itrhx.com/')print(type(response)) # 响应类型print(response.status) # 返回结果的状态码,200代表请求成功print(response.getheaders()) # 响应的头信息print(response.getheader('Server')) # 获取响应头的 server 值 运行结果:1234<class 'http.client.HTTPResponse'>200[('Content-Type', 'text/html; charset=utf-8'), ('Server', 'GitHub.com'), ('Last-Modified', 'Sat, 17 Aug 2019 12:16:48 GMT'), ('ETag', '\"5d57f030-10863\"'), ('Access-Control-Allow-Origin', '*'), ('Expires', 'Sat, 17 Aug 2019 19:41:25 GMT'), ('Cache-Control', 'max-age=600'), ('X-Proxy-Cache', 'MISS'), ('X-GitHub-Request-Id', 'C748:735D:5B7461:619B95:5D58560B'), ('Content-Length', '67683'), ('Accept-Ranges', 'bytes'), ('Date', 'Sun, 18 Aug 2019 13:28:44 GMT'), ('Via', '1.1 varnish'), ('Age', '228'), ('Connection', 'close'), ('X-Served-By', 'cache-tyo19931-TYO'), ('X-Cache', 'HIT'), ('X-Cache-Hits', '1'), ('X-Timer', 'S1566134924.190474,VS0,VE0'), ('Vary', 'Accept-Encoding'), ('X-Fastly-Request-ID', '25a69f8130fc9cae412d28990a724543d7d05e8b')]GitHub.com 【1.2.1.2】 添加参数根据 urlopen() 函数的 API 可知,除了最基本的 URL 参数以外,我们还可以传递其他内容,比如 data(附加数据)、timeout(超时时间)等,以下用 data 和 timeout 参数举例说明。 ● data 参数如果要添加 data 参数,需要使用 bytes 方法将参数转化为字节流编码格式的内容,即 bytes 类型。另外,如果传递了这个参数,则它的请求方式就不再是 GET 方式,而是 POST 方式。代码示例:123456import urllib.parseimport urllib.requestdata = bytes(urllib.parse.urlencode({'word': 'hello'}), encoding='utf8')response = urllib.request.urlopen('http://httpbin.org/post', data=data)print(response.read()) httpbin.org 站点提供 HTTP 请求测试,http://httpbin.org/post 用于测试 POST 请求,示例中传递一个值为 hello 的 word 参数。使用 bytes 方法,将其转码成 bytes(字节流)类型。该方法的第一个参数需要是 str(字符串)类型,需要用 urllib.parse 模块里的 urlencode 方法来将参数字典转化为字符串;第二个参数指定编码格式为 utf8,运行结果:123456789101112131415161718b'{ \"args\": {}, \"data\": \"\", \"files\": {}, \"form\": { \"word\": \"hello\" }, \"headers\": { \"Accept-Encoding\": \"identity\", \"Content-Length\": \"10\", \"Content-Type\": \"application/x-www-form-urlencoded\", \"Host\": \"httpbin.org\", \"User-Agent\": \"Python-urllib/3.6\" }, \"json\": null, \"origin\": \"171.115.101.10, 171.115.101.10\", \"url\": \"https://httpbin.org/post\"}' ● timeout 参数举例:1234import urllib.requestresponse = urllib.request.urlopen('http://httpbin.org/get', timeout=0.1) print(response.read()) 运行结果:12345678...During handling of the above exception, another exception occurred:Traceback (most recent call last): File \"C:/Users/Lenovo/Desktop/1.py\", line 2, in <module> response = urllib.request.urlopen('http://httpbin.org/get', timeout=0.1) ...urllib.error.URLError: <urlopen error timed out> timeout 设置为0.1,0.1秒过后服务器没有响应,便会抛出 URLError 异常进阶:使用 try except 语句抛出异常 【1.2.2】 urllib.request.Request()Request() 方法可以在请求的时候传入一些 data、headers 等信息Request() 的构造方法:1class urllib.request.Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None) 构造方法各个参数的解释: url:用于请求 URL,这是必传参数,其他都是可选参数。 data:如果要传,必须传 bytes(字节流)类型的。如果它是字典,可以先用 urllib.parse 模块里的 urlencode() 编码。 headers:是一个字典,它就是请求头,可以在构造请求时通过 headers 参数直接构造,也可以通过调用请求实例的 add_header() 方法添加。添加请求头最常用的用法就是通过修改 User-Agent 来伪装浏览器,默认的 User-Agent 是 Python-urllib,我们可以通过修改它来伪装浏览器。 origin_req_host:指的是请求方的 host 名称或者 IP 地址。 unverifiable:表示这个请求是否是无法验证的,默认是 False,意思就是说用户没有足够权限来选择接收这个请求的结果。例如,我们请求一个 HTML 文档中的图片,但是我们没有自动抓取图像的权限,这时 unverifiable 的值就是 True。 method:是一个字符串,用来指示请求使用的方法,比如 GET、POST 和 PUT 等。 简单举例:1234567891011121314import urllib.requestimport urllib.parseurl = 'http://www.baidu.com/'# 定制要伪装的头部headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}# 构建请求对象request = urllib.request.Request(url=url, headers=headers)# 发送请求response = urllib.request.urlopen(request)print(response.read().decode()) 【1.2.3】 urllib.request.urlretrieve()将获取到的 URL 内容保存到当前文件夹,简单举例:123456789import urllib.requesturl = 'https://www.itrhx.com/images/trhx.png'# response = urllib.request.urlopen(image_url)# with open('trhx.png', 'wb') as fp:# fp.write(response.read())urllib.request.urlretrieve(url, 'trhx.png') 【1.3】 urllib.error 异常处理【1.3.1】 URLError如果打开一个不存在的页面,就会出现 URLError 错误,该错误有一个 reason 属性,用于返回错误的原因。简单举例:12345from urllib import request, error try: response = request.urlopen('https://www.itrhx.com/index/') except error.URLError as e: print(e.reason) 输出结果:1Not Found 【1.3.2】 HTTPErrorURLError 的子类,专门用来处理 HTTP 请求错误,比如认证请求失败等。它有如下3个属性: code:返回 HTTP 状态码,比如 404 表示网页不存在,500 表示服务器内部错误等。 reason:同父类一样,用于返回错误的原因。 headers:返回请求头。 简单举例:12345from urllib import request, error try: response = request.urlopen('https://www.itrhx.com/index/') except error.HTTPError as e: print(e.code, e.reason, e.headers) 输出结果:123456789101112131415161718404 Not Found Content-Type: text/html; charset=utf-8Server: GitHub.comETag: \"5d57f030-7f2\"Access-Control-Allow-Origin: *X-Proxy-Cache: MISSX-GitHub-Request-Id: 4B46:2F5D:6DE0F1:755BB2:5D5964C5Content-Length: 2034Accept-Ranges: bytesDate: Sun, 18 Aug 2019 14:50:41 GMTVia: 1.1 varnishAge: 252Connection: closeX-Served-By: cache-tyo19951-TYOX-Cache: HITX-Cache-Hits: 1X-Timer: S1566139842.563134,VS0,VE0Vary: Accept-EncodingX-Fastly-Request-ID: e9eb0a507be66a866bfaa7c5cc2e1c53b1f7ccab 【1.3.3】 进阶用法因为 URLError 是 HTTPError 的父类,所以可以先选择捕获子类的错误,再去捕获父类的错误,前面的代码改进:12345678910from urllib import request, error ​try: response = request.urlopen('https://www.itrhx.com/index/') except error.HTTPError as e: print(e.reason, e.code, e.headers) except error.URLError as e: print(e.reason) else: print('Request Successfully') 【1.4】 urllib.parse 解析 URL【1.4.1】 urllib.parse.urlencode()将字典参数序列化为 GET 请求参数,示例:12345678from urllib.parse import urlencodedata = { 'ie': 'utf-8', 'wd': 'TRHX',}base_url = 'http://www.baidu.com?'url = base_url + urlencode(data)print(url) 输出结果:1http://www.baidu.com?ie=utf-8&wd=TRHX 【1.4.2】 urllib.parse.parse_qs()与 urlencode() 相反,将 GET 请求参数反序列化转回字典,示例:123from urllib.parse import parse_qsquery = 'name=TRHX&age=20'print(parse_qs(query)) 输出结果:1{'name': ['TRHX'], 'age': ['20']} 【1.4.3】 urllib.parse.parse_qsl()将参数转化为元组组成的列表,示例:123from urllib.parse import parse_qslquery = 'name=TRHX&age=20'print(parse_qsl(query)) 输出 结果:1[('name', 'TRHX'), ('age', '20')] 【1.4.4】 urllib.parse.urlparse()对 URL 进行分段,返回 6 个结果,示例:123from urllib.parse import urlparseresult = urlparse('http://www.baidu.com/index.html;user?id=5#comment')print(type(result), result) 输出结果:1<class 'urllib.parse.ParseResult'> ParseResult(scheme='http', netloc='www.baidu.com', path='/index.html', params='user', query='id=5', fragment='comment') 返回结果为 ParseResult 类型的对象,含 scheme、netloc、path、params、query 和 fragment 6 个部分,依次代表协议、域名、路径、参数、查询条件、锚点 【1.4.5】 urllib.parse.urlunparse()与 urlparse() 相反,对 URL 进行组合,传入的参数是一个可迭代对象,长度必须是 6,否则会抛出参数数量不足或者过多的问题,示例:123from urllib.parse import urlunparse data = ['http', 'www.baidu.com', 'index.html', 'user', 'a=6', 'comment'] print(urlunparse(data)) 输出结果:1http://www.baidu.com/index.html;user?a=6#comment 【1.4.6】 urllib.parse.urlsplit()与 urlparse() 方法相似,但是它不再单独解析 params 部分,只返回 5 个结果。params 会合并到 path 中,示例:123from urllib.parse import urlsplit result = urlsplit('http://www.baidu.com/index.html;user?id=5#comment') print(result) 输出结果:1SplitResult(scheme='http', netloc='www.baidu.com', path='/index.html;user', query='id=5', fragment='comment') 【1.4.7】 urllib.parse.urlunsplit()与 urlunparse() 方法类似,对 URL 进行组合,传入的参数也是一个可迭代对象,长度必须为 5,示例:123from urllib.parse import urlunsplit data = ['http', 'www.baidu.com', 'index.html', 'a=6', 'comment'] print(urlunsplit(data)) 输出结果:1http://www.baidu.com/index.html?a=6#comment 【1.4.8】 urllib.parse.urljoin()对 URL 进行组合,提供两个 URL 作为两个参数,将会自动分析 URL 的 scheme、netloc 和 path 这 3 个内容并对新链接缺失的部分进行补充,最后返回结果,示例:123456789from urllib.parse import urljoin print(urljoin('http://www.baidu.com', 'friends.html')) print(urljoin('http://www.baidu.com', 'https://www.itrhx.com/friends.html')) print(urljoin('http://www.baidu.com/friends.html', 'https://www.itrhx.com/friends.html')) print(urljoin('http://www.baidu.com/friends.html', 'https://www.itrhx.com/friends.html?id=2')) print(urljoin('http://www.baidu.com?wd=trhx', 'https://www.itrhx.com/index.html')) print(urljoin('http://www.baidu.com', '?category=2#comment')) print(urljoin('www.baidu.com', '?category=2#comment')) print(urljoin('www.baidu.com#comment', '?category=2')) 输出结果:12345678http://www.baidu.com/friends.htmlhttps://www.itrhx.com/friends.htmlhttps://www.itrhx.com/friends.htmlhttps://www.itrhx.com/friends.html?id=2https://www.itrhx.com/index.htmlhttp://www.baidu.com?category=2#commentwww.baidu.com?category=2#commentwww.baidu.com?category=2 【1.4.9】 urllib.parse.quote()将内容转化为 URL 编码的格式。当 URL 中带有中文参数时,可以将中文字符转化为 URL 编码,示例:1234from urllib.parse import quotekeyword = '中国' url = 'https://www.baidu.com/s?wd=' + quote(keyword) print(url) 输出结果:1https://www.baidu.com/s?wd=%E4%B8%AD%E5%9B%BD 【1.4.10】 urllib.parse.unquote()与 quote() 方法相反,对 URL 进行解码,示例:123from urllib.parse import unquote url = 'https://www.baidu.com/s?wd=%E4%B8%AD%E5%9B%BD' print(unquote(url)) 输出结果:1https://www.baidu.com/s?wd=中国 【1.5】 urllib.robotparser 爬取权限判断【1.5.1】 Robots 协议简介 Robots 协议即爬虫协议,用来告诉爬虫和搜索引擎哪些页面可以抓取,哪些不可以抓取。它通常是一个叫作 robots.txt 的文本文件,一般放在网站的根目录下。 robots.txt 基本格式:123User-agent:Disallow:Allow: User-agent 为搜索爬虫的名称,设置为 * 则表示对任何爬虫皆有效; Disallow 指定了不允许抓取的目录,设置为 / 则代表不允许抓取所有页面; Allow 指定了允许抓取的目录,一般和 Disallow 一起使用,一般不会单独使用,用来排除某些限制。 一些常见的搜索爬虫名称及其对应的网站: 爬虫名称 网站名称 网站地址 BaiduSpider 百度 www.baidu.com Googlebot 谷歌 www.google.com 360Spider 360 www.so.com Sogouspider 搜狗 www.sogou.com YodaoBot 有道 www.youdao.com Bingbot 必应 www.bing.com Yahoo! Slurp 雅虎 www.yahoo.com ia_archiver Alexa www.alexa.cn Scooter altavista www.altavista.com 【1.5.2】 RobotFileParser 类常用方法RobotFileParser 类的声明:1urllib.robotparser.RobotFileParser(url='') 常用方法及其解释: set_url:用来设置 robots.txt 文件的链接。如果在创建 RobotFileParser对象时传入了链接,那么就不需要再用这种方法了。 read:读取 robots.txt 文件并进行分析。此方法执行一个读取和分析操作,若不调用此方法,接下来的判断都会为 False,这个方法不会返回任何内容,但是执行了读取操作。 parse:解析 robots.txt 文件,传入的参数是 robots.txt 某些行的内容,它会按照 robots.txt 的语法规则来分析这些内容。 can_fetch:该方法传入两个参数,第一个是 User-agent,第二个是要抓取的 URL。返回的内容是该搜索引擎是否可以抓取这个 URL,返回结果是 True 或 False。 mtime:返回的是上次抓取和分析 robots.txt 的时间,此方法可以定期检查来抓取最新的 robots.txt。 modified:将当前时间设置为上次抓取和分析 robots.txt 的时间。 以简书为例:123456from urllib.robotparser import RobotFileParserrp = RobotFileParser()rp.set_url('http://www.jianshu.com/robots.txt')rp.read()print(rp.can_fetch('*', 'https://www.jianshu.com/p/6d9527300b4c'))print(rp.can_fetch('*', \"http://www.jianshu.com/search?q=python&page=1&type=collections\")) 输出结果:12FalseFalse","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"爬虫学习","slug":"Python3-学习笔记/爬虫学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/爬虫学习/"}],"tags":[{"name":"爬虫","slug":"爬虫","permalink":"https://www.itrhx.com/tags/爬虫/"},{"name":"urllib","slug":"urllib","permalink":"https://www.itrhx.com/tags/urllib/"}]},{"title":"一个 JS 脚本实现网站预加载,提升页面加载速度","slug":"A24-instant.page","date":"2019-08-23T01:27:49.948Z","updated":"2019-09-09T13:44:52.383Z","comments":true,"path":"2019/08/23/A24-instant.page/","link":"","permalink":"https://www.itrhx.com/2019/08/23/A24-instant.page/","excerpt":"instant.page 使用即时预加载技术,在用户点击之前预先加载页面。当用户的鼠标悬停在一个链接上超过 65 毫秒时,浏览器会对此页面进行预加载,当用户点击链接后,就从预加载的缓存中直接读取页面内容,从而达到缩短页面加载时间的目的。","text":"instant.page 使用即时预加载技术,在用户点击之前预先加载页面。当用户的鼠标悬停在一个链接上超过 65 毫秒时,浏览器会对此页面进行预加载,当用户点击链接后,就从预加载的缓存中直接读取页面内容,从而达到缩短页面加载时间的目的。 以我博客为例,使用了这项技术后,当鼠标在一个链接停留超过 65 毫秒时,Network 里可以看见相关文章已经预加载出来了,而停留时间过短就不会预加载(红色部分,状态为 canceled) 使用方法:将以下HTML代码放在</ body> 之前即可:1<script src=\"//instant.page/1.2.2\" type=\"module\" integrity=\"sha384-2xV8M5griQmzyiY3CDqh1dn4z3llDVqZDqzjzcY+jCBCk/a5fXJmuZ/40JJAPeoU\"></script> 但是此脚本是官方的,储存在国外服务器,对国内访问不太友好,可以将该JS脚本储存到自己的服务器上,点此获取该JS脚本,然后再根据以下格式在</ body> 之前引用:1<script src=\"`存放路径`/instantclick-1.2.2.js\" type=\"module\"></script> 也可以直接使用我的,使用 jsDeliver CDN 加速,速度还可以:1<script src=\"https://cdn.jsdelivr.net/gh/TRHX/CDN-for-itrhx.com@2.0.2/js/instantclick-1.2.2.js\" type=\"module\"></script> 参考资料:《网站预加载 JS 脚本 instant.page》——by 左岸 ;instant.page官网","categories":[{"name":"WEB前端","slug":"WEB前端","permalink":"https://www.itrhx.com/categories/WEB前端/"}],"tags":[{"name":"instant.page","slug":"instant-page","permalink":"https://www.itrhx.com/tags/instant-page/"},{"name":"JS 预加载","slug":"JS-预加载","permalink":"https://www.itrhx.com/tags/JS-预加载/"}]},{"title":"网站ICP备案和公安备案流程","slug":"A23-beian","date":"2019-08-23T01:27:49.803Z","updated":"2020-03-14T06:07:46.135Z","comments":true,"path":"2019/08/23/A23-beian/","link":"","permalink":"https://www.itrhx.com/2019/08/23/A23-beian/","excerpt":"","text":"网站备案分为ICP备案和公安备案 ICP备案:ICP备案的目的就是为了防止在网上从事非法的网站经营活动,打击不良互联网信息的传播,如果网站不备案的话,很有可能被查处以后关停。根据中华人民共和国信息产业部第十二次部务会议审议通过的《非经营性互联网信息服务备案管理办法》条例,在中华人民共和国境内提供非经营性互联网信息服务,应当办理备案。未经备案,不得在中华人民共和国境内从事非经营性互联网信息服务。而对于没有备案的网站将予以罚款或关闭。 公安备案:网站备案是根据国家法律法规需要网站的所有者向国家有关部门申请的备案,公安局备案是其中一种。公安局备案一般按照各地公安机关指定的地点和方式进行,操作流程会比ICP备案流程简单,主要是已登记为主。 以百度官网为例,其中京公安网备11000002000001就是公安备案,京ICP证030173号就是ICP备案 – ICP备案 一般在域名服务商那里都会有代备案系统,下面以阿里云为例,进入备案系统: 1、填写信息验证备案类型备案主办单位填写,个人就选个人,企业就选企业,按照实际信息填写: 2、产品验证对搭建备案网站的云服务器进行验证,如果你在阿里云购买了相关产品,就选择相应的产品类型和实例进行验证,也可以勾选已有备案服务号,填写服务号进行验证,备案服务号可以通过备案控制台进行申请,具体操作可以参考官方文档《申请备案服务号》,也有的小伙伴没有在任何地方购买过服务器等相关产品,比如单纯搭建一个 Github Pages + Hexo 轻量级的个人博客,这种博客没有后端,不需要服务器,但是要备案怎么办?这种情况也好解决,去某宝买一个服务号就行了。 3、填写网站信息填写网站信息以及办理备案的个人或者单位的真实信息,在填写网站名称的时候要特别注意!特别注意!特别注意!不满足要求的话是会被打回的!不能使用姓名、地名、成语、不能包含公司、组织等企业性质的词语……具体要求可以参考官方文档《填写主体信息和网站信息》。 4、上传资料根据要求,上传证件照片或证件彩色扫描件。身份证好说,拍好了上传就行了,注意《网站备案信息真实性核验单》需要你下载并打印在一张A4纸上,使用黑色签字笔填写,不能涂改,具体可参照所给的示例进行填写,填写完成后再拍照上传。企业网站类似,提交备案后会在一个工作日内进行初审。 5、人脸核验或幕布拍照核验根据不同地域管局要求及核验平台的支持情况,使用人脸识别进行核验,或者申请专用幕布进行幕布拍照核验 地区 核验要求 上海、福建地区用户 需使用阿里云APP进行人脸核验。如果使用PC端发起的备案申请,请根据界面提示下载阿里云APP进行人脸核验。 广东、辽宁、安徽、重庆地区用户 首次备案、新增网站:支持使用阿里云APP进行人脸核验或通过阿里云备案平台(PC端)进行幕布拍照核验。其他备案类型:需通过阿里云备案平台(PC端)进行幕布拍照核验。 其他地区用户 通过阿里云备案平台(PC端)进行幕布拍照核验。 以幕布拍照核验为例,如果你没有阿里云的幕布,就需要申请幕布(免费的),邮寄很快,大约两三天就到了,等收到幕布后,按照要求进行拍照,一定要仔细阅读拍照说明!一定要仔细阅读拍照说明!一定要仔细阅读拍照说明!不合格依旧会被打回!拍照完成后上传即可。 6、提交管局、短信核验当照片审核通过后,就会提交到管局,工信部要求部分省市成为手机号码短信核验试点省市,相应省市的用户在阿里云备案平台提交备案申请且初审完成后,会收到工信部发送的核验短信,短信包含验证码和验证地址,需要在收到短信的24小时内完成短信核验,备案申请才能进入管局审核。需短信核验省份: 2017年12月18日起:天津、甘肃、西藏、宁夏、海南、新疆、青海被列为试点省份。 2018年9月10日起:浙江、四川、福建、陕西、重庆、广西、云南被列为试点省份。 2018年9月24日起:山东、河南、安徽、湖南、山西、黑龙江、内蒙古、湖北被列为试点省份。 7、ICP备案完成整个备案过程中会有阿里云的客服打电话给你,进行信息确认,备案申请信息成功提交管局系统后,管局审核一般为 3 - 20 个工作日(亲测很快,不到一个周就通过了),审核通过后会收到阿里云的邮件通知。 – 公安备案 公安备案个人觉得比ICP备案还要麻烦,自己在公安备案的时候,最开始申请了一个月也没给我处理(大概是地方原因,所在的市比较小,估计都没几个人办过网站,网警也不太负责),与ICP备案最大的不同,如果你是交互式网站的话,公安备案是需要你去公安机关当面审核的,这也是比较麻烦的一点。 1、用户注册、登录登录全国互联网安全管理服务平台,选择联网备案登录,注册账号并登录 2、新办网站备案申请点击新办网站申请,按实填写网站开办主体,上传身份证正反照和手持身份证件照。 3、填写网站基本信息按实填写网站基本信息,需要注意的地方: IP:IP地址为阿里云/腾讯云的公网IP地址,请不要填写内网IP。 域名证书:以阿里云为例,进入【域名控制台】,点击域名后面的【管理】,选择【域名证书下载】即可,其它服务商类似。 网络接入/域名注册服务商:若办理公安备案的域名是通过阿里云完成的工信部备案,则按照以下填写:网络接入服务商: 接入商所属地区管辖:境内 接入商所属区域 :浙江省 杭州市 滨江区 名称:阿里云计算有限公司 网站接入方式:租赁虚拟空间 域名注册服务商: 域名商所属地区管辖:境内 域名服务商所属区域:浙江省 杭州市 余杭区 名称:阿里云计算有限公司(原万网) 也可以通过点击后面的查询网络接入\\域名注册服务商直接选择相应服务商,其他服务商类似 服务类型:交互式服务指:为互联网用户提供信息发布、交流互动等服务,包括但不限于论坛、博客、微博、网络购物、网上支付等服务类型,此项选择是否提供互联网交互服务将会直接影响到后面是否需要去公安局当面核验,若选择是,当地网警会打电话叫你去公安局当面核验,还需要填写《交互式服务安全检查表》等各种文件,总之是比较麻烦的,个人小网站,博客什么的建议选择否,选择www服务,这样的话不用去当面核验,审核下来也比较快,企业单位用户建议选择交互式。 其他信息如实填写即可! 4、填写网站负责人信息填写网站安全负责人和网站应急联络人相关信息,网站应急联络人直接勾选同主体负责人后会自动填入。 5、同意责任书并提交审核《互联网信息服务单位网络安全责任告知书》有30秒的强制阅读时间,建议认真阅读一下告知书的内容。然后勾选我已阅读,点击提交即可。随后可以看到审核状态,不同地区政策有所不同,会有当地的网警联系网站负责人的,审核通过后记得在网站首页底部张贴公安机关核发的备案图标!","categories":[{"name":"Hexo","slug":"Hexo","permalink":"https://www.itrhx.com/categories/Hexo/"},{"name":"WEB前端","slug":"WEB前端","permalink":"https://www.itrhx.com/categories/WEB前端/"}],"tags":[{"name":"ICP备案","slug":"ICP备案","permalink":"https://www.itrhx.com/tags/ICP备案/"},{"name":"公安备案","slug":"公安备案","permalink":"https://www.itrhx.com/tags/公安备案/"}]},{"title":"恶意刷留言者——你是什么垃圾?","slug":"A25-SB","date":"2019-08-23T01:27:46.962Z","updated":"2020-03-14T06:11:57.038Z","comments":true,"path":"2019/08/23/A25-SB/","link":"","permalink":"https://www.itrhx.com/2019/08/23/A25-SB/","excerpt":"","text":"有一种动物,自认为自己技术了得,实则和CXK差不多,以攻击他人为乐,这种动物称为程序员中的垃圾,哦!不,这种动物称不上程序员! 这个周连续被人刷垃圾评论,具体开始时间不记得了,不想多说什么,太多的文字用在垃圾身上简直是玷污中华上下五千年的文化,只问一句,你是什么垃圾? 随便提一句,这家伙连我的情侣博客一起刷的,真是让人大跌眼镜啊,估计自己没女朋友吧,见不得别人好,悲催啊,不知道又是哪个学校,哪个公司,哪个家庭摊上了这种垃圾。 请问你是什么垃圾?垃圾分类,从我做起!","categories":[{"name":"BLOG","slug":"BLOG","permalink":"https://www.itrhx.com/categories/BLOG/"}],"tags":[{"name":"垃圾","slug":"垃圾","permalink":"https://www.itrhx.com/tags/垃圾/"}]},{"title":"利用官方支持为基于GitHub Pages的Hexo博客启用HTTPS","slug":"A28-hexo-add-https","date":"2019-08-11T14:16:09.175Z","updated":"2019-12-31T15:24:54.792Z","comments":true,"path":"2019/08/11/A28-hexo-add-https/","link":"","permalink":"https://www.itrhx.com/2019/08/11/A28-hexo-add-https/","excerpt":"利用官方支持为基于GitHub Pages的Hexo博客启用HTTPS","text":"利用官方支持为基于GitHub Pages的Hexo博客启用HTTPS HTTP(超文本传输协议),是一个基于请求与响应,无状态的,应用层的协议,常基于TCP/IP协议传输数据,互联网上应用最为广泛的一种网络协议,所有的WWW文件都必须遵守这个标准。设计HTTP的初衷是为了提供一种发布和接收HTML页面的方法。 HTTPS(超文本传输安全协议),是以安全为目标的HTTP通道,简单讲是HTTP的安全版。即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。它是一个URI scheme(抽象标识符体系),句法类同http:体系。用于安全的HTTP数据传输。 目前大多数基于 GitHub Pages 的 Hexo 博客都是利用 CloudFlare 的 CDN 中转来启用 HTTPS 的,实现方法可以参考我的文章:《利用Cloudflare为基于GitHub Pages的Hexo博客添加HTTPS支持》,这样的做法确实可以起到开启HTTPS的目的,但是这样做也有弊端,你会发现 CDN 中转,国外访问的话,可以起到加速的作用,但是国内访问反而速度降低了,还不如直接连接GitHub呢 其实 GitHub 官方是支持自定义域名开启 HTTPS 的,之前我和大多数人一样,以为只有 GitHub Pages 自带的域名(xxx.github.io)才能开启 HTTPS,直到有一天我发现了官方在2018年5月1日发表的博客:《Custom domains on GitHub Pages gain support for HTTPS》,大概讲的意思就是从8月份开始, GitHub Pages 上的自定义域名也能开启 HTTPS 了,下面就具体介绍一下如何实现 如果你以前域名的记录类型是 CNAME 方式,那么就不需要做任何更改如果你以前域名的记录类型是 A 方式,那么就需要把记录值指向以下IP地址: 185.199.108.153 185.199.109.153 185.199.110.153 185.199.111.153 修改好记录值后,我们需要再次来到你博客的 GitHub 仓库,在仓库的【Settings】- 【GitHub Pages】下勾选【Enforce HTTPS】,注意,如果此时你不能勾选,请删除【Custom domain】里面你的域名并点击【Save】保存,刷新网页后就可以勾选了,然后在把域名填进去并保存即可,短时间可能会出现不安全的提示,这是因为加密证书大概一个小时左右才会生效,等一会儿就好了 最后贴一个我的域名解析,可作为参考:","categories":[{"name":"Hexo","slug":"Hexo","permalink":"https://www.itrhx.com/categories/Hexo/"}],"tags":[{"name":"Hexo","slug":"Hexo","permalink":"https://www.itrhx.com/tags/Hexo/"},{"name":"HTTPS","slug":"HTTPS","permalink":"https://www.itrhx.com/tags/HTTPS/"}]},{"title":"Github+jsDelivr+PicGo 打造稳定快速、高效免费图床","slug":"A27-image-hosting","date":"2019-07-31T16:02:08.497Z","updated":"2020-06-08T03:31:04.290Z","comments":true,"path":"2019/08/01/A27-image-hosting/","link":"","permalink":"https://www.itrhx.com/2019/08/01/A27-image-hosting/","excerpt":"","text":"– 前言图床是个啥东西就不用过多介绍了,先来对比一下各路图床: 微博图床:以前用的人比较多,从2019年4月开始开启了防盗链,凉凉 SM.MS:运营四年多了,也变得越来越慢了,到了晚上直接打不开图片,速度堪忧 其他小众图床:随时有挂掉的风险 Imgur等国外图床:国内访问速度太慢,随时有被墙的风险 大厂储存服务:例如七牛云、又拍云、腾讯云COS、阿里云OSS等,容量限制,操作繁琐,又是实名认证又是域名备案的,麻烦,而且还要花钱(有钱又不怕麻烦的当我没说) 因此,GitHub 图床是个不错的选择,利用 jsDelivr CDN 加速访问(jsDelivr 是一个免费开源的 CDN 解决方案),PicGo 工具一键上传,操作简单高效,GitHub 和 jsDelivr 都是大厂,不用担心跑路问题,不用担心速度和容量问题,而且完全免费,可以说是目前免费图床的最佳解决方案! – 新建GitHub仓库登录/注册GitHub,新建一个仓库,填写好仓库名,仓库描述,根据需求选择是否为仓库初始化一个README.md描述文件 – 生成一个Token在主页依次选择【Settings】-【Developer settings】-【Personal access tokens】-【Generate new token】,填写好描述,勾选【repo】,然后点击【Generate token】生成一个Token,注意这个Token只会显示一次,自己先保存下来,或者等后面配置好PicGo后再关闭此网页 – 配置PicGo前往下载PicGo,安装好后开始配置图床 设定仓库名:按照【用户名/图床仓库名】的格式填写 设定分支名:【master】 设定Token:粘贴之前生成的【Token】 指定存储路径:填写想要储存的路径,如【ITRHX-PIC/】,这样就会在仓库下创建一个名为 ITRHX-PIC 的文件夹,图片将会储存在此文件夹中 设定自定义域名:它的作用是,在图片上传后,PicGo 会按照【自定义域名+储存路径+上传的图片名】的方式生成访问链接,并放到粘贴板上,因为我们要使用 jsDelivr 加速访问,所以可以设置为【https://cdn.jsdelivr.net/gh/用户名/图床仓库名 】,上传完毕后,我们就可以通过【https://cdn.jsdelivr.net/gh/用户名/图床仓库名/图片路径 】加速访问我们的图片了,比如上图的图片链接为:https://cdn.jsdelivr.net/gh/TRHX/ImageHosting/ITRHX-PIC/A27/08.jpg 关于 jsDelivr 具体是如何引用资源的可以参考我的另一篇博客:《免费CDN:jsDelivr+Github》 – 进行高效创作配置好PicGo后,我们就可以进行高效创作了,将图片拖拽到上传区,将会自动上传并复制访问链接,将链接粘贴到博文中就行了,访问速度杠杠的,此外PicGo还有相册功能,可以对已上传的图片进行删除,修改链接等快捷操作,PicGo还可以生成不同格式的链接、支持批量上传、快捷键上传、自定义链接格式、上传前重命名等,更多功能自己去探索吧!","categories":[{"name":"图床","slug":"图床","permalink":"https://www.itrhx.com/categories/图床/"}],"tags":[{"name":"jsDelivr","slug":"jsDelivr","permalink":"https://www.itrhx.com/tags/jsDelivr/"},{"name":"图床","slug":"图床","permalink":"https://www.itrhx.com/tags/图床/"},{"name":"PicGo","slug":"PicGo","permalink":"https://www.itrhx.com/tags/PicGo/"}]},{"title":"利用Cloudflare为基于GitHub Pages的Hexo博客添加HTTPS支持","slug":"A26-hexo-add-https","date":"2019-07-31T03:48:43.215Z","updated":"2020-03-14T06:13:57.930Z","comments":true,"path":"2019/07/31/A26-hexo-add-https/","link":"","permalink":"https://www.itrhx.com/2019/07/31/A26-hexo-add-https/","excerpt":"利用Cloudflare为基于GitHub Pages的Hexo博客添加HTTPS支持","text":"利用Cloudflare为基于GitHub Pages的Hexo博客添加HTTPS支持 HTTP(超文本传输协议),是一个基于请求与响应,无状态的,应用层的协议,常基于TCP/IP协议传输数据,互联网上应用最为广泛的一种网络协议,所有的WWW文件都必须遵守这个标准。设计HTTP的初衷是为了提供一种发布和接收HTML页面的方法。 HTTPS(超文本传输安全协议),是以安全为目标的HTTP通道,简单讲是HTTP的安全版。即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。它是一个URI scheme(抽象标识符体系),句法类同http:体系。用于安全的HTTP数据传输。 – 前言GitHub Pages 自带的域名(xxx.github.io)支持开启 https 服务,可以在仓库的【Settings】- 【GitHub Pages】下勾选【Enforce HTTPS】即可,但是如果你设置了自定义域名的话,就比较复杂了,因为 hexo 博客是托管在 GitHub 上的,没有自己的服务器,因此也不支持上传 SSL 证书,从2018年5月1日起,GitHub官方也支持自定义域名开启https了,实现方法可参考我的文章:《利用官方支持为基于GitHub Pages的Hexo博客启用HTTPS》,另外一种方法就是利用 Cloudflare 的 CDN 中转来启用 HTTPS,这种方法的弊端就是国内访问速度可能会变慢,本文主要讲述这种方法 Cloudflare 是一家美国的跨国科技企业,以向客户提供网站安全管理、性能优化及相关的技术支持为主要业务,它提供了免费的 https 服务,注意不是应用SSL证书,实现原理:用户到CDN服务器的连接为 https 方式,而CDN服务器到 GithubPages 服务器的连接为 http 方式,在CDN服务器那里加上反向代理 – 注册 Cloudflare到 Cloudflare官网 注册账号 – 添加站点添加你的站点,一直下一步即可 如果你已经在域名服务商那里解析过域名的话,之后就会出现你域名的解析列表,如果还没有解析过,可以参考《为hexo博客配置个性域名》 –修改DNS点击下一步 Cloudflare 会提供给你两个 DNS 地址 到域名服务商那里修改DNS,以阿里云为例,依次选择【控制台】-【域名】,选择你的域名,点击【管理】-【修改DNS】,将上面 Cloudflare 提供的两个 DNS 地址填进去,会过几分钟才生效 –开启 HTTPS在 Cloudflare 管理页面,点击【Crypto】选项,选择 SSL 的模式为【full】,注意:在CloudFlare 上激活站点后,可能需要24小时才能颁发新证书,耐心等待即可 关于三种模式 Flexible、Full、Full (Strict) 的区别: Flexible:访客与 Cloudflare 之间是加密的,Cloudflare 到站点服务器是不加密的 Full:访客到 Cloudflare、Cloudflare 到站点服务器都是加密的,它不会验证你服务器上的证书是否合法,因此你可以在你服务器上安装任何证书,包括自签名证书 Full (strict):访客到 Cloudflare、Cloudflare 到站点服务器都是加密的,它会验证你服务器上的证书是否合法,你必须在你的服务器上安装有可信赖的CA证书,并且这个证书必须是未过期,包含有域名等信息的 至此,我们的域名就支持 https 访问了,但是当用户输入 http://xxxxxx 访问时,浏览器依旧会以 http 协议来访问,并不会跳转到 https,这时候就需要利用重定向来解决了 –重定向强制 HTTPSCloudflare 提供了一个名叫 Page Rules 的页面规则的功能,我们可以利用此功能对 URL 做一些处理,当用户访问是 HTTP 的时候重定向到 HTTPS,点击【Page Rules】选项,点击【Create Page Rules】,新建如下规则并保存即可 现在我们的 Hexo 博客就实现了全站 HTTPS!","categories":[{"name":"Hexo","slug":"Hexo","permalink":"https://www.itrhx.com/categories/Hexo/"}],"tags":[{"name":"Hexo","slug":"Hexo","permalink":"https://www.itrhx.com/tags/Hexo/"},{"name":"HTTPS","slug":"HTTPS","permalink":"https://www.itrhx.com/tags/HTTPS/"}]},{"title":"Eclipse 通过 JDBC 连接 SQL Server","slug":"A22-eclipse-connects-to-sql","date":"2019-05-13T18:21:23.785Z","updated":"2020-03-14T05:59:46.063Z","comments":true,"path":"2019/05/14/A22-eclipse-connects-to-sql/","link":"","permalink":"https://www.itrhx.com/2019/05/14/A22-eclipse-connects-to-sql/","excerpt":"","text":"本文用到的软件版本以及相关环境: Eclipse Photon Release (4.8.0)JDK-10.0.2SQL Server 2012 1.配置 SQL Server 2012打开 SQL Server Management Studio,使用 SQL Server 身份验证 登录: 如果在安装 SQL Server 2012 时选用了Windows身份验证登录方式,则需要重新设置,设置方法参考:《SQL Server 登录更换【Windows身份验证】为【SQL Server 身份验证】》 登录成功后,打开 SQL Server 配置管理器: 在左边找到 SQL Server 网络配置,点击【你的数据库名】的协议,将右边栏的 Shared Memory、Named Pipes、TCP/IP 全部右键选择启用: 双击 TCP/IP(或者右键选择属性),选择【IP地址】,将【IP1】和【IP10】的【IP地址】设为 127.0.0.1 将所有【IPx】(IP1、IP10、IP11、IP12等)的【已启用】设为是 下拉到窗口底部,将 【IPAll】 中的【TCP端口】设成 1433,其余不变 2.开启 Telnet 服务打开【控制面板】,选择【程序】,点击【启用或关闭 Windows 功能】,找到【Telnet Client】勾选并保存,Windows 7 或者以下的版本则勾选【Telnet 服务器】和【Telnet 客户端】 3.测试1433端口是否打开 运行cmd,输入 telnet 127.0.0.1 1433,若提示连接失败,则说明1433端口没有打开,需要重新进行以上配置,若连接成功,则显示如下: 4.下载JDBC 点击此处下载各个版本JDBC,不同版本的JDBC驱动程序适用的JAR不同,与不同版本的SQL兼容性也不同,具体参考《Microsoft SQL Server JDBC 驱动程序支持矩阵》,比如使用 SQL Server 2012 我们可以下载6.0的版本,下载sqljdbc_6.0.8112.200_chs.tar.gz文件,解压后可以找到sqljdbc41.jar与sqljdbc42.jar文件,使用时要注意自己JDK是哪个版本的,1.80以上的则对应 sqljdbc42.jar 类库 5.Eclipse 连接 SQL Server将 sqljdbc41.jar 或者 sqljdbc42.jar 放到一个文件夹下,打开 Eclipse,在需要连接数据库的项目里,右键【src】,选择【Build Path】、【Configure Build Path…】,在弹出的窗口选择【Libraries】,选择【Modulepath】,单击【Add External JARs…】,找到下载的 sqljdbc41.jar 或者 sqljdbc42.jar 文件并打开,然后【Apply and Close】保存 6.测试连接打开 SQL Server 2012,在其中新建数据库 test Eclipse中,在项目下新建一个package,再新建一个class,用于测试数据库的连接:12345678910111213141516171819202122package test;import java.sql.*;public class Main { public static void main(String [] args) { String driverName=\"com.microsoft.sqlserver.jdbc.SQLServerDriver\"; String dbURL=\"jdbc:sqlserver://localhost:1433;DatabaseName=test\"; //要连接的数据库名 String userName=\"sa\"; //数据库用户名 String userPwd=\"000000\"; //数据库密码 try { Class.forName(driverName); Connection dbConn=DriverManager.getConnection(dbURL,userName,userPwd); System.out.println(\"连接数据库成功\"); } catch(Exception e) { e.printStackTrace(); System.out.print(\"连接失败\"); } } } 如果以上所有操作正确,就能成功连接数据库了:","categories":[{"name":"Java","slug":"Java","permalink":"https://www.itrhx.com/categories/Java/"}],"tags":[{"name":"JDBC","slug":"JDBC","permalink":"https://www.itrhx.com/tags/JDBC/"},{"name":"SQL Server 2012","slug":"SQL-Server-2012","permalink":"https://www.itrhx.com/tags/SQL-Server-2012/"},{"name":"Elicpse","slug":"Elicpse","permalink":"https://www.itrhx.com/tags/Elicpse/"}]},{"title":"Python PEP8 代码规范常见问题及解决方法","slug":"A21-PEP8","date":"2019-04-14T17:09:58.738Z","updated":"2019-09-24T12:47:14.895Z","comments":true,"path":"2019/04/15/A21-PEP8/","link":"","permalink":"https://www.itrhx.com/2019/04/15/A21-PEP8/","excerpt":"之前一直用 Python IDLE 写代码,最近换成 PyCharm 写代码总是会出现波浪号,这才了解到 Python 的 PEP8 代码规范,所以将常见的 PEP8 代码规范问题和解决方法记录一下,学习一下,遇到了再持续更新,养成良好的习惯,编写规范的代码!","text":"之前一直用 Python IDLE 写代码,最近换成 PyCharm 写代码总是会出现波浪号,这才了解到 Python 的 PEP8 代码规范,所以将常见的 PEP8 代码规范问题和解决方法记录一下,学习一下,遇到了再持续更新,养成良好的习惯,编写规范的代码! PEP 8: no newline at end of file解决方法:代码末尾需要另起一行,光标移到最后回车即可 PEP 8: indentation is not a multiple of four解决方法:缩进不是4的倍数,检查缩进 PEP 8: over-indented解决方法:过度缩进,检查缩进 PEP 8: missing whitespace after’,’解决方法:逗号后面少了空格,添加空格即可,类似还有分号或者冒号后面少了空格 PEP 8: multiple imports on one line解决方法:不要在一句 import 中引用多个库,举例:import socket, urllib.error最好写成:import socket import urllib.error PEP 8: blank line at end of line解决方法:代码末尾行多了空格,删除空格即可 PEP 8: at least two spaces before inline comment解决方法:代码与注释之间至少要有两个空格 PEP 8: block comment should start with ‘#’解决方法:注释要以#加一个空格开始 PEP 8: inline comment should start with ‘#’解决方法:注释要以#加一个空格开始 PEP 8: module level import not at top of file解决方法:import不在文件的最上面,可能之前还有其它代码 PEP 8: expected 2 blank lines,found 0解决方法:需要两条空白行,添加两个空白行即可 PEP 8: function name should be lowercase解决方法:函数名改成小写即可 PEP 8: missing whitespace around operator解决方法:操作符(’=’、’>’、’<’等)前后缺少空格,加上即可 PEP 8: unexpected spaces around keyword / parameter equals解决方法:关键字/参数等号周围出现意外空格,去掉空格即可 PEP 8: multiple statements on one line (colon)解决方法:多行语句写到一行了,比如:if x == 2: print('OK')要分成两行写 PEP 8: line too long (82 > 79 characters)解决方法:超过了每行的最大长度限制79 如果想要选择性忽略PEP8代码风格的警告信息可以使用以下方法:(养成良好的习惯,编写规范的代码!不推荐忽略!) ①将鼠标移到出现警告信息的地方,按 alt+Enter,选择忽略(Ignore)这个错误即可:②依次选择 File - Settings - Editor - Inspections,在 Python下找到 PEP8 coding style violation 选项,在右下角的 Ignore errors 里点击加号可以添加需要忽略的警告信息ID(ID信息见后面附录),例如想要忽略indentation contains mixed spaces and tabs这个警告,只需要添加其ID:E101 即可附录:全部警告信息以及对应的ID,官方地址:https://pep8.readthedocs.io/en/latest/intro.html#error-codes code sample message E1 Indentation E101 indentation contains mixed spaces and tabs E111 indentation is not a multiple of four E112 expected an indented block E113 unexpected indentation E114 indentation is not a multiple of four (comment) E115 expected an indented block (comment) E116 unexpected indentation (comment) E117 over-indented E121 (*^) continuation line under-indented for hanging indent E122 (^) continuation line missing indentation or outdented E123 (*) closing bracket does not match indentation of opening bracket’s line E124 (^) closing bracket does not match visual indentation E125 (^) continuation line with same indent as next logical line E126 (*^) continuation line over-indented for hanging indent E127 (^) continuation line over-indented for visual indent E128 (^) continuation line under-indented for visual indent E129 (^) visually indented line with same indent as next logical line E131 (^) continuation line unaligned for hanging indent E133 (*) closing bracket is missing indentation E2 Whitespace E201 whitespace after ‘(‘ E202 whitespace before ‘)’ E203 whitespace before ‘:’ E211 whitespace before ‘(‘ E221 multiple spaces before operator E222 multiple spaces after operator E223 tab before operator E224 tab after operator E225 missing whitespace around operator E226 (*) missing whitespace around arithmetic operator E227 missing whitespace around bitwise or shift operator E228 missing whitespace around modulo operator E231 missing whitespace after ‘,’, ‘;’, or ‘:’ E241 (*) multiple spaces after ‘,’ E242 (*) tab after ‘,’ E251 unexpected spaces around keyword / parameter equals E261 at least two spaces before inline comment E262 inline comment should start with ‘# ‘ E265 block comment should start with ‘# ‘ E266 too many leading ‘#’ for block comment E271 multiple spaces after keyword E272 multiple spaces before keyword E273 tab after keyword E274 tab before keyword E275 missing whitespace after keyword E3 Blank line E301 expected 1 blank line, found 0 E302 expected 2 blank lines, found 0 E303 too many blank lines (3) E304 blank lines found after function decorator E305 expected 2 blank lines after end of function or class E306 expected 1 blank line before a nested definition E4 Import E401 multiple imports on one line E402 module level import not at top of file E5 Line length E501 (^) line too long (82 > 79 characters) E502 the backslash is redundant between brackets E7 Statement E701 multiple statements on one line (colon) E702 multiple statements on one line (semicolon) E703 statement ends with a semicolon E704 (*) multiple statements on one line (def) E711 (^) comparison to None should be ‘if cond is None:’ E712 (^) comparison to True should be ‘if cond is True:’ or ‘if cond:’ E713 test for membership should be ‘not in’ E714 test for object identity should be ‘is not’ E721 (^) do not compare types, use ‘isinstance()’ E722 do not use bare except, specify exception instead E731 do not assign a lambda expression, use a def E741 do not use variables named ‘l’, ‘O’, or ‘I’ E742 do not define classes named ‘l’, ‘O’, or ‘I’ E743 do not define functions named ‘l’, ‘O’, or ‘I’ E9 Runtime E901 SyntaxError or IndentationError E902 IOError W1 Indentation warning W191 indentation contains tabs W2 Whitespace warning W291 trailing whitespace W292 no newline at end of file W293 blank line contains whitespace W3 Blank line warning W391 blank line at end of file W5 Line break warning W503 (*) line break before binary operator W504 (*) line break after binary operator W505 (*^) doc line too long (82 > 79 characters) W6 Deprecation warning W601 .has_key() is deprecated, use ‘in’ W602 deprecated form of raising exception W603 ‘<>’ is deprecated, use ‘!=’ W604 backticks are deprecated, use ‘repr()’ W605 invalid escape sequence ‘x’ W606 ‘async’ and ‘await’ are reserved keywords starting with Python 3.7","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"学习经验","slug":"Python3-学习笔记/学习经验","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/学习经验/"}],"tags":[{"name":"Python","slug":"Python","permalink":"https://www.itrhx.com/tags/Python/"},{"name":"PEP8","slug":"PEP8","permalink":"https://www.itrhx.com/tags/PEP8/"}]},{"title":"VMware Pro 15 安装 Deepin15.9 国产操作系统","slug":"A20-install-deepin15.9","date":"2019-04-14T12:53:34.310Z","updated":"2020-03-14T05:53:55.551Z","comments":true,"path":"2019/04/14/A20-install-deepin15.9/","link":"","permalink":"https://www.itrhx.com/2019/04/14/A20-install-deepin15.9/","excerpt":"","text":"Deepin是由武汉深之度科技有限公司开发的Linux发行版,个人认为其界面设计非常美观,而且作为国产操作系统,值得我们去体验和支持! 1.下载安装 VMware Workstation Pro 15 进入 VMware 官网或者在软件商店下载最新版VMware虚拟机并安装 2.下载 Deepin15.9 系统 进入 deepin 官网,下载最新版 deepin 系统镜像 3.在 VMware 中创建虚拟机打开安装好的 VMware Workstation Pro 15,选择创建新的虚拟机在新建虚拟机向导中选择自定义(高级):默认直接下一步,直到出现下图,再选择稍后安装操作系统:选择客户机操作系统为 Linux ,如果你电脑是32位就选择 Ubuntu 版本,64位就选择 Ubuntu 64 位版本:更改虚拟机名称及存放位置:接下来为虚拟机指定处理器数量、分配内存(太大了可能会导致卡顿,太小了也不好,推荐内存大小即可)一直选择默认即可,选择磁盘时,选择创建新虚拟磁盘:选择将虚拟磁盘储存为单个文件:默认下一步:点击完成:此时我们就可以在虚拟机左侧“我的计算机”下面看到刚刚创建的虚拟机 Deepin,单击 Deepin,选择“编辑虚拟机设置”, 再选择“CD/DVD(SATA)”,选择“使用ISO映像文件”,点击“浏览”,找到先前我们下载好的 Deepin 15.9 镜像文件,点击“确定” 4.在虚拟机上安装 Deepin 系统单击 Deepin,选择“开启此虚拟机”接下来就是选择语言、创建用户、选择时区、指定磁盘等过程:安装完成后:可以看见界面还是相当美观的,系统也自带了深度的一些软件,比如深度录屏,深度录音,深度影院,深度计算器等等的一些小工具,作为国产操作系统,个人觉得已经非常优秀了,值得去体验!","categories":[{"name":"Linux","slug":"Linux","permalink":"https://www.itrhx.com/categories/Linux/"}],"tags":[{"name":"VMware","slug":"VMware","permalink":"https://www.itrhx.com/tags/VMware/"},{"name":"Deepin","slug":"Deepin","permalink":"https://www.itrhx.com/tags/Deepin/"}]},{"title":"Windows 系统中 Pygame 的安装","slug":"A19-install-pygame","date":"2019-03-10T14:34:09.591Z","updated":"2019-09-24T12:47:07.382Z","comments":true,"path":"2019/03/10/A19-install-pygame/","link":"","permalink":"https://www.itrhx.com/2019/03/10/A19-install-pygame/","excerpt":"","text":"Pygame是跨平台Python模块,专为电子游戏设计,可用于管理图形、动画乃至声音,建立在SDL基础上,允许实时电子游戏研发而无需被低级语言(如机器语言和汇编语言)束缚,通过使用Pygame来处理在屏幕上绘制图像等任务,你不用考虑众多繁琐而艰难的编码工作,而是将重点放在程序的高级逻辑上。 你可以从以下三个地址查找与你运行的Python版本相匹配的Windows安装程序: https://bitbucket.org/pygame/pygame/downloads/ (Pygame项目托管在代码分享网站Bitbucket中) http://www.pygame.org/download.shtml (Pygame官网) https://www.lfd.uci.edu/~gohlke/pythonlibs/#pygame (如果以上两个地址找不到合适的安装程序,推荐去这个) 如果下载的是.exe文件,直接运行它,如果下载的是.whl文件,就需要打开命令窗口,切换到该文件所在的目录,使用pip来运行它: 首先检查电脑是否安装了pip,打开终端窗口,执行如下命令:1>python -m pip --version 如果输出版本信息则已安装:1>pip 18.1 from E:\\Python\\lib\\site-packages\\pip (python 3.6) 否则请安装pip,访问 https://bootstrap.pypa.io/get-pip.py ,如果出现对话框请直接保存文件,如果出现的是get-pip.py的源代码,则需要新建一个get-pip.py文件,将该代码复制粘贴到其中,使用下面的命令运行get-pip.py:1>python get-pip.py 安装完成后可再次使用python -m pip --version命令检查是否成功安装了pip,成功安装pip后,使用以下命令来安装Pygame:(注意要先cd到你下载的文件的目录)1>python -m pip install --user 下载的.whl文件名 出现以下信息则表示安装成功:1>Successfully installed 你安装的Pygame版本 比如我的Python版本是3.6.5,64位的,则需要下载pygame‑1.9.4‑cp36‑cp36m‑win_amd64.whl,该文件保存到了桌面,使用下面的命令安装Pygame:12345678C:\\Users\\Lenovo>cd desktopC:\\Users\\Lenovo\\Desktop>python -m pip install --user pygame‑1.9.4‑cp36‑cp36m‑win_amd64.whlProcessing c:\\users\\lenovo\\desktop\\pygame‑1.9.4‑cp36‑cp36m‑win_amd64.whlInstalling collected packages: pygameSuccessfully installed pygame‑1.9.4C:\\Users\\Lenovo>Desktop> 检查是否成功安装Pygame:在Python的IDLE里输入import pygame,如果不报错,则安装成功,再输入pygame.ver就能看到版本号: ) 可能出现的问题:报错:xxxxxxxxxxxxxxxxxxxxxx.whl is not a supported wheel on this platform.原因:Python版本与Pygame版本不对应解决方法:Pygame文件名中的cp**表示Python对应的版本,另外并不是你电脑64位则下载64位,要看你安装的Python是否为64位,注意下载对应的版本! 报错:You are using pip version x.x.x, however version x.x.x is available.You should consider upgrading via the 'python -m pip install --upgrade pip' command.原因:版本需要更新解决方法:输入python -m pip install --upgrade pip命令进行更新即可 Pygame安装完成后我们就可以使用Python来开发游戏啦!","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"学习经验","slug":"Python3-学习笔记/学习经验","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/学习经验/"}],"tags":[{"name":"Python","slug":"Python","permalink":"https://www.itrhx.com/tags/Python/"},{"name":"Pygame","slug":"Pygame","permalink":"https://www.itrhx.com/tags/Pygame/"}]},{"title":"免费CDN:jsDelivr + Github","slug":"A18-free-cdn","date":"2019-02-10T14:30:17.903Z","updated":"2020-03-14T05:51:31.881Z","comments":true,"path":"2019/02/10/A18-free-cdn/","link":"","permalink":"https://www.itrhx.com/2019/02/10/A18-free-cdn/","excerpt":"","text":"本文有参考《jsDelivr+github使用教程,免费好用的cdn》—— By hojun CDN的全称是Content Delivery Network,即内容分发网络。CDN是构建在网络之上的内容分发网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率。CDN的关键技术主要有内容存储和分发技术。——百度百科 放在Github的资源在国内加载速度比较慢,因此需要使用CDN加速来优化网站打开速度,jsDelivr + Github便是免费且好用的CDN,非常适合博客网站使用。 1、新建Github仓库 2、克隆Github仓库到本地 点击 Clone or download,一键复制仓库地址 在本地目录右键 Git Bash Here,执行以下命令: 1git clone 一键复制的仓库地址 3、上传资源 复制需要上传的资源到本地git仓库(注:jsDelivr不支持加载超过20M的资源),在本地git仓库目录下右键 Git Bash Here,执行以下命令:1234git status //查看状态git add . //添加所有文件到暂存区git commit -m '第一次提交' //把文件提交到仓库git push //推送至远程仓库 4、发布仓库 点击release发布 自定义发布版本号 5、通过jsDelivr引用资源 使用方法:https://cdn.jsdelivr.net/gh/你的用户名/你的仓库名@发布的版本号/文件路径例如:https://cdn.jsdelivr.net/gh/TRHX/CDN-for-itrhx.com@1.0/images/trhx.png    https://cdn.jsdelivr.net/gh/TRHX/CDN-for-itrhx.com@2.0.1/css/style.css    https://cdn.jsdelivr.net/gh/moezx/cdn@3.1.3//The%20Pet%20Girl%20of%20Sakurasou.mp4 注意:版本号不是必需的,是为了区分新旧资源,如果不使用版本号,将会直接引用最新资源,除此之外还可以使用某个范围内的版本,查看所有资源等,具体使用方法如下: // 加载任何Github发布、提交或分支https://cdn.jsdelivr.net/gh/user/repo@version/file // 加载 jQuery v3.2.1https://cdn.jsdelivr.net/gh/jquery/jquery@3.2.1/dist/jquery.min.js // 使用版本范围而不是特定版本https://cdn.jsdelivr.net/gh/jquery/jquery@3.2/dist/jquery.min.jshttps://cdn.jsdelivr.net/gh/jquery/jquery@3/dist/jquery.min.js // 完全省略该版本以获取最新版本https://cdn.jsdelivr.net/gh/jquery/jquery/dist/jquery.min.js // 将“.min”添加到任何JS/CSS文件中以获取缩小版本,如果不存在,将为会自动生成https://cdn.jsdelivr.net/gh/jquery/jquery@3.2.1/src/core.min.js // 在末尾添加 / 以获取资源目录列表https://cdn.jsdelivr.net/gh/jquery/jquery/","categories":[{"name":"CDN","slug":"CDN","permalink":"https://www.itrhx.com/categories/CDN/"}],"tags":[{"name":"jsDelivr","slug":"jsDelivr","permalink":"https://www.itrhx.com/tags/jsDelivr/"},{"name":"CDN","slug":"CDN","permalink":"https://www.itrhx.com/tags/CDN/"}]},{"title":"新年快乐!","slug":"A17-happy-new-year","date":"2019-02-04T19:09:51.832Z","updated":"2019-09-09T14:11:08.281Z","comments":true,"path":"2019/02/05/A17-happy-new-year/","link":"","permalink":"https://www.itrhx.com/2019/02/05/A17-happy-new-year/","excerpt":"","text":"C printf("2019,祝大家"); C++ cout<<"一帆风顺"; C# System.Console.WriteLine("二龙腾飞") VB Msg("三羊开泰") VC MessageBox("四季平安"); Java System.out.println("五福临门"); JavaScript alert("六六大顺") PHP echo "七星高照"; Python print("八方来财") Html <br/>九运当头<br/> Objectivec NSLog(@"十全十美"); QBasic Print "阖家幸福" Asp Response.Write "心想事成" Ruby puts "财源广进" VBScript MsgBox "幸福安康" XML <TextView android:text="大展宏图" /> LUA print("学业有成") Delphi ShowMessage('万事如意'); shell echo 步步高升 perl print '鸿案齐眉' LISP (format t "身体健康~%") powerBuilder messagebox("龙马精神") COBOL DISPLAY '笑口常开' aswing JOptionPane.showMessageDialog("happy","好运连连") Android Toast.makeText(getApplicationContext(),"年年有余",Toast.LENGTH_SHORT).show() flex Alert.show("大吉大利"); Foxpro ?[家庭幸福!] iapp tw("瑞气盈门") DOS批处理 echo 鹏程万里 易语言 调试输出(“万事亨通”) Clojure (println "年年有今昔") verilog/systemverilog/e $display("岁岁有今朝") as trace("祝大家新年快乐!");","categories":[{"name":"BLOG","slug":"BLOG","permalink":"https://www.itrhx.com/categories/BLOG/"}],"tags":[{"name":"BLOG","slug":"BLOG","permalink":"https://www.itrhx.com/tags/BLOG/"}]},{"title":"一台电脑使用两个/多个GitHub账号部署两个/多个Hexo博客","slug":"A16-deploy-two-or-more-hexo-blogs","date":"2019-01-18T13:23:24.345Z","updated":"2020-02-07T07:30:20.946Z","comments":true,"path":"2019/01/18/A16-deploy-two-or-more-hexo-blogs/","link":"","permalink":"https://www.itrhx.com/2019/01/18/A16-deploy-two-or-more-hexo-blogs/","excerpt":"由于个人原因需要在一台电脑上部署两个Hexo博客,本来以为挺简单,没想到问题重重,首先是一个GitHub账号只能搭建一个Hexo博客,因此就需要使用其他GitHub账号;其次是一台电脑绑定两个GitHub账号,则需要两对公钥,在处理第二个问题时遇到的问题比较多,因为对这方面一窍不通,还是小白,所以折腾了一下午才解决,网上好多教程我都看不懂,觉得不(自)够(己)详(太)细(笨),因此详细记录一下","text":"由于个人原因需要在一台电脑上部署两个Hexo博客,本来以为挺简单,没想到问题重重,首先是一个GitHub账号只能搭建一个Hexo博客,因此就需要使用其他GitHub账号;其次是一台电脑绑定两个GitHub账号,则需要两对公钥,在处理第二个问题时遇到的问题比较多,因为对这方面一窍不通,还是小白,所以折腾了一下午才解决,网上好多教程我都看不懂,觉得不(自)够(己)详(太)细(笨),因此详细记录一下 原理分析: SSH的公钥是GitHub作为本地仓库和远程仓库连接的唯一标识,一个公钥只能对应一个GitHub账户,如果将一个相同的公钥上传到不同的GitHub账户,GitHub则无法做出辨识,进而导致错误 一台电脑,可以生成多对公私钥,可以通过配置,将不同的公钥上传到不同的GitHub账号,那么就不存在单个公钥绑定多个GitHub账号的情况存在了 相关问题报错: 同一台电脑部署第二个Hexo博客执行hexo g -d时报错:ERROR: Permission to xxxxxx/xxxxxx.github.io.git denied to xxxxxx. 添加新的 SSH 密钥 到 SSH agent 执行ssh-add xxx时报错:Could not open a connection to your authentication agent. 单独设置用户名/邮箱时报错:fatal: not in a git directory 以下是详细过程:前提:假设你的第二个博客相关配置操作已经顺利完成,但使用hexo g -d命令部署到 GitHub 上时报错:ERROR: Permission to xxxxxx/xxxxxx.github.io.git denied to xxxxxx. - 查看当前密钥首先我们打开终端输入ls ~/.ssh/可以查看当前已有的密钥,显示id_rsa 与 id_rsa_pub说明已经有一对密钥 - 创建新的密钥首先使用以下命令进入 SSH根目录下:1cd ~/.ssh/ 方法一直接使用以下命令创建新密钥,然后两次回车即可:1ssh-keygen -t rsa -f ~/.ssh/这里是新密钥名称 -C \"这里是你的邮箱\" 注意区别新密钥名称和旧密钥名称,不要相同!!! 方法二使用下面命令行创建新密钥:1ssh-keygen -t rsa -C \"这里是你的邮箱\" 回车后会出现:12Generating public/private rsa key pair. Enter file in which to save the key (/c/Users/you/.ssh/id_rsa): 注意此时需要你输入新密钥的名称,同样要注意区别新密钥名称和旧密钥名称,不要相同!!!之后再两次回车,新密钥创建完毕! - 配置config查看你的.ssh/根路径下, 有没有config文件,( 比如我的路径为C:\\Users\\Lenovo.ssh)没有则使用以下命令创建一个config文件:1touch config 用记事本或者其他工具打开config文件(注意config文件是没有任何后缀名的),写入以下配置: 1234567891011#第一个账号,默认使用的账号,不用做任何更改Host github.com HostName github.com User git IdentityFile ~/.ssh/id_rsa #第二个新账号,#\"xxxxxx\"为前缀名,可以任意设置,要记住,后面需要用到Host xxxxxx.github.com HostName github.com User git IdentityFile ~/.ssh/这里是你创建的新密钥的名称 - 设置新GitHub账户SSH key输入以下命令复制你创建的公钥:1clip < ~/.ssh/这里是你创建的新密钥的名称.pub 也可以直接在.ssh目录下找到你创建的新的公钥,文件名为新密钥的名称.pub,(比如我的是trhx_rsa.pub),用记事本打开,复制里面的内容,然后打开你的新GitHub账号主页,依次进入Settings —> SSH and GPG keys —> New SSH key,将刚复制的内容粘贴到Key那里,Title可以随便填,点击Add Key保存。 - 清空本地的 SSH 缓存,添加新的 SSH 密钥 到 SSH agent中使用命令cd ~/.sshcd到.ssh根目录下,依次执行以下命令: 123ssh-add -Dssh-add xxxxxx #旧密钥名称,一般是id_rsassh-add xxxxxx #新创建的密钥名称 如果执行以上命令出现错误:Could not open a connection to your authentication agent.,那么就需要先执行ssh-agent bash,再执行以上命令 - 验证配置是否成功依次执行以下命令,第一个为默认ssh_key验证;第二个为新的ssh_key验证,其中“xxxxxx”为你先前在config文件中的命名12ssh -T git@github.comssh -T git@xxxxxxx.github.com 依次显示以下信息, 则说明配置成功:1Hi 你的用户名! You've successfully authenticated, but GitHub does not provide shell access. - 取消全局用户名/邮箱配置,单独设置用户名/邮箱执行如下命令,取消全局用户名和邮箱配置(如果已经设置了全局的话): 12git config --global --unset user.namegit config --global --unset user.email 分别进入你的两个Hexo博客.git目录下执行以下命令单独设置用户名/邮箱:12git config user.name \"这里是用户名\"git config user.email \"这里是你的邮箱\" 如果此时报错:fatal: not in a git directory,说明你没有进入.git目录下,具体路径:\\Hexo\\.deploy_git\\.git,.git目录是隐藏的,需要你设置隐藏目录可见 执行以下命令可以查看设置是否成功1git config --list - hexo 配置文件修改git地址打开你的第二个博客Hexo目录下的_config.yml文件,找到deploy关键字,写入以下配置并保存:1234deploy: type: git repository: git@xxxxxx.github.com:你的用户名/你的用户名.github.io.git branch: master 比如我的配置:1234deploy: type: git repository: git@love109.github.com:love109/love109.github.io.git branch: master 大功告成,再次执行hexo g -d就能成功将新的博客部署到 Github 上了","categories":[{"name":"Hexo","slug":"Hexo","permalink":"https://www.itrhx.com/categories/Hexo/"}],"tags":[{"name":"Hexo","slug":"Hexo","permalink":"https://www.itrhx.com/tags/Hexo/"},{"name":"Github","slug":"Github","permalink":"https://www.itrhx.com/tags/Github/"}]},{"title":"Python3 基础学习笔记 C09","slug":"A15-Python3-basic-C09","date":"2018-11-15T16:46:13.649Z","updated":"2019-09-24T12:45:51.693Z","comments":true,"path":"2018/11/16/A15-Python3-basic-C09/","link":"","permalink":"https://www.itrhx.com/2018/11/16/A15-Python3-basic-C09/","excerpt":"Python3 基础学习笔记第九章 —— 【文件和异常】","text":"Python3 基础学习笔记第九章 —— 【文件和异常】 - 9.1 从文件中读取数据 - 9.1.1 读取整个文件 有一个文件,包含精确到小数点后30位的圆周率值,且在小数点后每10位处都换行:12345Circumference rate.txt----------3.1415926535 8979323846 2643383279 以下两个程序将打开并读取这个文件,再将其内容显示到屏幕上:12345#file_reader.pywith open('Circumference rate.txt') as file_object: contents = file_object.read() print(contents) 12345#file_reader2.pycontents = open ('Circumference rate.txt')print(contents.read())contents.close() 函数open()接受一个参数:要打开的文件的名称,Python在当前执行的文件所在的目录中查找指定的文件;关键字with在不再需要访问文件后将其关闭;也可以调用open()和close()来打开和关闭文件,如果使用这种方法,当程序存在bug时,close()语句未执行,文件将不会被关闭;方法read()将读取这个文件的全部内容,并将其作为一个长长的字符串储存在变量contents中,通过打印contents的值,就可以将这个文本文件的全部内容打印出来:1233.1415926535 8979323846 2643383279 输出结果末尾有一空行,这是因为read()到达末尾时返回一个空字符串,而将这个空字符串显示出来就是一个空行,如果要删除末尾的空行,可在print语句中使用rstrip():12345#file_reader.pywith open('Circumference rate.txt') as file_object: contents = file_object.read() print(contents.rstrip()) 输出结果如下:1233.1415926535 8979323846 2643383279 - 9.1.2 文件路径 相对文件路径:假定程序文件位于python_work文件夹中,程序文件操作的文本文件位于python_work文件夹的子文件夹text_files中,此时可以使用相对文件路径来打开该文本文件,相对文件路径让Python到指定的位置去查找,而该位置是相对于当前运行的程序所在目录的 在Linux和OS X中,相对路径类似于如下:1with open('text_files/filename.txt') as file_object: 在Windows系统中,文件路径中使用反斜杠(\\)而不是斜杠(/):1with open('text_files\\filename.txt') as file_object: 绝对文件路径:不用关心当前运行的程序储存在什么地方,直接将文件在计算机中的准确位置告诉Python,这称为绝对文件路径,绝对路径通常比相对路径更长,因此将其储存在一个变量中,再将变量传递给open()会有所帮助 在Linux和OS X中,绝对路径类似于如下:12file_path = '/home/ehmatthes/other_files/text_files/filename.txt'with open(file_path) as file_object: 在Windows系统中,绝对路径类似于如下:12file_path = 'C:\\Users\\ehmatthes\\other_files\\text_files\\filename.txt'with open(file_path) as file_object: - 9.1.3 逐行读取 要以每次一行的方式检查文件,可对文件对象使用for循环:123456#file_reader.pyfilename = 'Circumference rate.txt'with open(filename) as file_object: for line in file_object: print(line) 在文件中每行的末尾都有一个看不见的换行符,而print语句也会加上一个换行符,因此每行末尾都有两个换行符:一个来自文件,一个来自print语句,输出结果如下:123453.1415926535 8979323846 2643383279 要消除这些多余的空白行,可以使用rstrip():123456#file_reader.pyfilename = 'Circumference rate.txt'with open(filename) as file_object: for line in file_object: print(line.rstrip()) 输出结果如下:1233.1415926535 8979323846 2643383279 - 9.1.4 创建一个包含文件各行内容的列表 使用关键字with时,open()返回的文件对象只在with代码块内可用,如果要在with代码块外访问文件的内容,可在with代码块内将文件的各行储存在一个列表当中,并在with代码块外使用该列表:12345678#file_reader.pyfilename = 'Circumference rate.txt'with open(filename) as file_object: lines = file_object.readlines() for line in lines: print(line.rstrip()) 输出结果与文件内容完全一致 - 9.1.5 使用文件的内容 创建一个字符串,它包含文件中储存的所有数字,且没有任何空格:123456789101112#pi_string.pyfilename = 'Circumference rate.txt'with open(filename) as file_object: lines = file_object.readlines()pi_string = ''for line in lines: pi_string += line.rstrip() print(pi_string)print(len(pi_string)) 打印该字符串以及其长度:123.1415926535 8979323846 264338327936 由于原文件每行左边都有空格,我们可以使用strip()而不是rstrip()来删除它:123456789101112#pi_string.pyfilename = 'Circumference rate.txt'with open(filename) as file_object: lines = file_object.readlines()pi_string = ''for line in lines: pi_string += line.strip() print(pi_string)print(len(pi_string)) 输出结果如下:123.14159265358979323846264338327932 Python中有三个去除头尾字符、空白符的函数,它们依次为: strip:用来去除头尾字符、空白符(包括\\n、\\r、\\t、’ ‘,即:换行、回车、制表符、空格) lstrip:用来去除开头字符、空白符(包括\\n、\\r、\\t、’ ‘,即:换行、回车、制表符、空格) rstrip:用来去除结尾字符、空白符(包括\\n、\\r、\\t、’ ‘,即:换行、回车、制表符、空格)注意:这些函数都只会删除头和尾的字符,中间的不会删除。用法分别为:string.strip([chars])string.lstrip([chars])string.rstrip([chars])参数chars是可选的,当chars为空,默认删除string头尾的空白符(包括\\n、\\r、\\t、’ ‘)当chars不为空时,函数会被chars解成一个个的字符,然后将这些字符去掉它返回的是去除头尾字符(或空白符)的string副本,string本身不会发生改变 - 9.2 写入文件 将一条简单的消息储存到文件中:12345#write_message.pyfilename = 'programming.txt'with open(filename,'w') as file_object: file_object.write(\"I love programming!\") 调用open()时提供了两个实参,第一个实参也是要打开文件的名称,第二个实参(’w’)告诉Python,我们要以写入模式打开这个文件,打开文件时,可指定读取模式(’r’)、写入模式(’w’)、附加模式(’a’)或者让我们能够读取和写入文件的模式(’r+’),如果省略模式实参,则默认以只读模式打开文件 附表:Python读写文件各种模式区别 模式 可做操作 若文件不存在 是否覆盖 r 打开一个文件用于只读 报错 - rb 以二进制格式打开一个文件用于只读 报错 - r+ 打开一个文件用于读和写 报错 是 rb+ 以二进制格式打开一个文件用于读和写 报错 是 w 打开一个文件用于只写 创建 是 wb 以二进制格式打开一个文件只用于只写 创建 是 w+ 打开一个文件用于读和写 创建 是 wb+ 以二进制格式打开一个文件用于读和写 创建 是 a 打开一个文件用于追加 创建 否,追加写 ab 以二进制格式打开一个文件用于追加 创建 否,追加写 a+ 打开一个文件用于读和写 创建 否,追加写 ab+ 以二进制格式打开一个文件用于追加 创建 否,追加写 - 9.3 使用 try-except 代码块处理异常 当我们尝试将一个数字除以0时,会发生ZeroDivisionError异常:12345>>> print(5/0)Traceback (most recent call last): File \"<pyshell#0>\", line 1, in <module> print(5/0)ZeroDivisionError: division by zero 此时我们可以编写一个try-except代码块来处理该异常:1234try: print(5/0)except ZeroDivisionError: print(\"You can't divide by zero!\") 当我们运行该程序时,会出现提示:1You can't divide by zero! 在try-except代码块中加入else,编写一个只执行除法运算的简单计算器:12345678910111213141516print(\"Give me two numbers,and I'll divide them.\")print(\"Enter 'q' to quit.\")while True: first_number = input(\"\\nFirst number:\") if first_number == 'q': break second_number = input(\"\\nSecond number:\") if second_number == 'q': break try: answer = int(first_number)/int(second_number) except ZeroDivisionError: print(\"You can't divide by 0!\") else: print(answer) 运行程序:1234567891011121314Give me two numbers,and I'll divide them.Enter 'q' to quit.First number:45Second number:0You can't divide by 0!First number:36Second number:84.5First number:q 若不加入try-except代码块,我们在输入0时,程序就会出现异常而崩溃,而try-except代码块很好的解决了这种问题,而且还起到了提示的作用,同样的,try-except代码块也可以处理其他异常,如FileNotFoundError等 - 9.4 储存数据 - 9.4.1 使用 json.dump() 和 json.load() 模块json能够将简单的Python数据结构转储到文件中,并在程序再次运行时加载该文件中的数据;编写一个储存一组数字的简短程序,再编写一个将这些数字读取到内存中的程序,第一个程序将使用 json.dump()来储存这组数据,而第二个程序将使用 json.load()。函数 json.dump()接受两个实参:要储存的数据以及可用于储存数据的文件对象:123456789#number_writer.pyimport jsonnumbers = [2,3,5,7,11,13]filename = 'numbers.json'with open(filename,'w') as f_obj: json.dump(numbers,f_obj) 先导入模块json,再创建一个数字列表, 通常用文件扩展名.json来指出文件储存的数据为JSON格式,然后以写入模式打开该文件,使用函数json.dump()将数字列表储存到文件numbers.json中,打开该文件,数据的储存格式与Python一样:1[2, 3, 5, 7, 11, 13] 再编写一个程序,使用json.load()将这个列表读取到内存中:12345678#number_reader.pyimport jsonfilename = 'numbers.json'with open(filename) as f_obj: numbers = json.load(f_obj)print(numbers) 输出结果与number_writer.py中创建的数字列表相同:1[2, 3, 5, 7, 11, 13] 进阶:在同一个程序中使用 json.dump() 和 json.load():创建文件username.json储存用户名,从该文件中获取用户名,如果这个文件不存在,就在except代码块中提示用户输入用户名,并将其储存在username.json中:1234567891011121314151617#remember_me.pyimport json#如果以前储存了用户名,就加载它#否则就提示用户输入用户名并储存它filename = 'numbers.json'try: with open(filename) as f_obj: username = json.load(f_obj)except FileNotFoundError: username = input(\"What's your name?\") with open(filename,'w') as f_obj: json.dump(username,f_obj) print(\"We'll remember you when you come back, \" + username + \"!\")else: print(\"Welcome back, \" + username + \"!\") 以前没有储存用户名,第一次运行程序:12What's your name?TRHXWe'll remember you when you come back, TRHX! 再次运行程序:1Welcome back, TRHX! - 9.4.2 重构 代码能够正确运行,但可以做进一步的改进——将代码划分为一系列完成具体工作的函数,这样的过程称为重构,重构让代码更清晰、更易于理解、更容易扩展重构remember_me.py,将大部分逻辑放到一个或者多个函数中:12345678910111213141516171819#remember_me.pyimport jsondef greet_user(): #问候用户,并指出其名字 filename = 'numbers.json' try: with open(filename) as f_obj: username = json.load(f_obj) except FileNotFoundError: username = input(\"What's your name?\") with open(filename,'w') as f_obj: json.dump(username,f_obj) print(\"We'll remember you when you come back, \" + username + \"!\") else: print(\"Welcome back, \" + username + \"!\")greet_user() 重构greet_user(),让它不执行这么多任务——将获取储存的用户名的代码移到另一个函数中:12345678910111213141516171819202122232425262728#remember_me.pyimport jsondef get_stored_username(): #如果储存了用户名,就获取它 filename = 'numbers.json' try: with open(filename) as f_obj: username = json.load(f_obj) except FileNotFoundError: return None else: return usernamedef greet_user(): #问候用户,并指出其名字 username = get_stored_username() if username: print(\"Welcome back, \" + username + \"!\") else: username = input(\"What's your name?\") filename = 'username.json' with open(filename,'w') as f_obj: json.dump(username,f_obj) print(\"We'll remember you when you come back, \" + username + \"!\") greet_user() 将greet_user()中的另一个代码块提取出来:将没有储存用户名时提示用户输入的代码放在一个独立的函数中:12345678910111213141516171819202122232425262728293031323334#remember_me.pyimport jsondef get_stored_username(): #如果储存了用户名,就获取它 filename = 'numbers.json' try: with open(filename) as f_obj: username = json.load(f_obj) except FileNotFoundError: return None else: return usernamedef get_new_username(): #提示输入用户名 username = input(\"What's your name?\") filename = 'username.json' with open(filename,'w') as f_obj: json.dump(username,f_obj) return username def greet_user(): #问候用户,并指出其名字 username = get_stored_username() if username: print(\"Welcome back, \" + username + \"!\") else: username = get_new_username() print(\"We'll remember you when you come back, \" + username + \"!\") greet_user() 最终版本实现了每个函数只负责单一而清晰的任务,我们在编写程序时也要像这样,要写出清晰而易于维护和扩展的代码","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"基础学习","slug":"Python3-学习笔记/基础学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/基础学习/"}],"tags":[{"name":"文件","slug":"文件","permalink":"https://www.itrhx.com/tags/文件/"},{"name":"异常","slug":"异常","permalink":"https://www.itrhx.com/tags/异常/"}]},{"title":"Python3 基础学习笔记 C08","slug":"A14-Python3-basic-C08","date":"2018-11-10T16:10:47.892Z","updated":"2019-09-24T12:45:48.188Z","comments":true,"path":"2018/11/11/A14-Python3-basic-C08/","link":"","permalink":"https://www.itrhx.com/2018/11/11/A14-Python3-basic-C08/","excerpt":"Python3 基础学习笔记第八章 —— 【类】","text":"Python3 基础学习笔记第八章 —— 【类】 - 8.1 创建类和使用类 创建一个表示小狗的简单类Dog,根据Dog类创建的每个实例都将储存名字和年龄,赋予每条小狗蹲下(sit())和打滚(roll_over())的能力: 1234567891011121314class Dog(): def __init__(self,name,age): #初始化属性name和age self.name = name self.age = age def sit(self): #模拟小狗被命令时蹲下 print(self.name.title() + \" is now sitting.\") def roll_over(self): #模拟小狗被命令时打滚 print(self.name.title() + \" rolled over!\") 方法init():类中的函数称为方法,本例中方法init()是一个特殊的方法,每当我们根据Dog类创建新实例时,Python都会自动运行它,在方法的名称中,开头和结尾各有两个下划线,这是一种约定,避免Python默认方法与普通方法发生名称冲突,例子中将方法init()定义成了包含三个形参:self、name和age,在这个方法的定义中,形参self必不可少,还必须位于其他形参的前面,Python调用方法init()来创建Dog实例时,将自动传入实参self,每个与类相关联的方法调用都自动传递实参self,它是一个指向实例本身的引用,让实例能够访问类中的属性和方法,我们创建Dog实例时,Python将调用Dog类的方法init(),我们将通过实参向Dog()传递名字和年龄;self会自动传递,因此我们不需要传递它,每当我们根据Dog类创建实例时,都只需要给最后两个形参(name和age)提供值;定义的两个变量都有前缀self,以self为前缀的变量都可以供类中的所有方法使用,还可以通过类的任何实例来访问这些变量。self.name = name 获取储存在形参name中的值,并将其储存到变量name中,然后该变量被关联到当前创建的实例。self.age = age 的作用与此类似,像这样可通过实例访问的变量称为属性;Dog还定义了另外两种方法:sit() 和 roll_over() ,由于这些方法不需要额外的信息,如名字和年龄,因此它们只有一个形参self 在Python 2.7中创建类时,需要在括号内包含单词object:12class ClassName(object): ---snip--- - 8.2 根据类创建实例访问属性:创建一个表示特定小狗的实例: 123456789101112131415161718class Dog(): def __init__(self,name,age): #初始化属性name和age self.name = name self.age = age def sit(self): #模拟小狗被命令时蹲下 print(self.name.title() + \" is now sitting.\") def roll_over(self): #模拟小狗被命令时打滚 print(self.name.title() + \" rolled over!\")my_dog = Dog('willie',6)print(\"My dog's name is \" + my_dog.name.title() + \".\")print(\"My dog is \" + str(my_dog.age) + \" years old.\") 让Python创建一条名字为’willie’,年龄为6的小狗,Python使用实参’willie’和6调用Dog类中的方法init()。方法init()创建一个表示特定小狗的示例,并使用我们提供的值来设置属性name和age;在访问实例的属性时,可使用句点表示法,比如该例子中的 my_dog.name;最终程序输出结果如下: 12My dog's name is Willie.My dog is 6 years old. 调用方法:根据Dog类创建实例后,就可以使用句点表示法来调用Dog类中定义的任何方法:123456789101112131415161718class Dog(): def __init__(self,name,age): #初始化属性name和age self.name = name self.age = age def sit(self): #模拟小狗被命令时蹲下 print(self.name.title() + \" is now sitting.\") def roll_over(self): #模拟小狗被命令时打滚 print(self.name.title() + \" rolled over!\")my_dog = Dog('willie',6)my_dog.sit()my_dog.roll_over() 输出结果如下:12Willie is now sitting.Willie rolled over! 创建多个实例:可按需求根据类创建任意数量的实例:12345678910111213141516171819202122232425class Dog(): def __init__(self,name,age): #初始化属性name和age self.name = name self.age = age def sit(self): #模拟小狗被命令时蹲下 print(self.name.title() + \" is now sitting.\") def roll_over(self): #模拟小狗被命令时打滚 print(self.name.title() + \" rolled over!\")my_dog = Dog('willie',6)your_dog = Dog('lucy',8)print(\"My dog's name is \" + my_dog.name.title() + \".\")print(\"My dog is \" + str(my_dog.age) + \" years old.\")my_dog.sit()print(\"\\nYour dog's name is \" + your_dog.name.title() + \".\")print(\"Your dog is \" + str(your_dog.age) + \" years old.\")your_dog.roll_over() 输出结果如下:1234567My dog's name is Willie.My dog is 6 years old.Willie is now sitting.Your dog's name is Lucy.Your dog is 8 years old.Lucy rolled over! - 8.3 使用类和实例 创建一个表示汽车的类,其中储存了有关汽车的信息,还有一个汇总这些信息的方法:12345678910111213class Car(): def __init__(self,make,model,year): self.make = make self.model = model self.year = year def get_descriptive_name(self): long_name = str(self.year) + ' ' + self.make + ' ' +self.model return long_name.title() my_new_car = Car('audi','a9','2018')print(my_new_car.get_descriptive_name()) 输出结果如下:12018 Audi A9 - 8.3.1 给属性指定默认值 类中的每个属性都必须有初始值,如果我们设置了默认值,就无需包含为它提供初始值的形参,下面为8.3的例子添加一个 odometer_reading 的属性,其初值是0,添加一个 odometer_reading() 方法,用于读取汽车的里程表: 123456789101112131415161718 class Car(): def __init__(self,make,model,year): self.make = make self.model = model self.year = year self.odometer_reading = 0 def get_descriptive_name(self): long_name = str(self.year) + ' ' + self.make + ' ' +self.model return long_name.title() def read_odomter(self): print(\"This car has \" + str(self.odometer_reading) + \" miles on it.\")my_new_car = Car('audi','a9','2018')print(my_new_car.get_descriptive_name())my_new_car.read_odomter() 输出结果如下:122018 Audi A9This car has 0 miles on it. - 8.3.2 修改属性的值 可以以三种不同的方式修改属性的值:直接通过实例进行修改;通过方法进行设置;通过方法进行递增(增加特定的值) 直接修改属性的值:要修改属性的值,最简单的方法就是通过实例直接访问它,将8.3.1中的例子第7行代码 self.odometer_reading = 0 改为 self.odometer_reading = 66,输出结果如下:12 2018 Audi A9This car has 66 miles on it. 通过方法修改属性的值:1234567891011121314151617181920212223class Car(): def __init__(self,make,model,year): self.make = make self.model = model self.year = year self.odometer_reading = 0 def get_descriptive_name(self): long_name = str(self.year) + ' ' + self.make + ' ' +self.model return long_name.title() def read_odomter(self): print(\"This car has \" + str(self.odometer_reading) + \" miles on it.\") def update_odometer(self,mileage): self.odometer_reading = mileage my_new_car = Car('audi','a9','2018')print(my_new_car.get_descriptive_name())my_new_car.update_odometer(66)my_new_car.read_odomter() 对Car类所做的唯一修改就是在第17、18行添加了方法 update_odometer(),这个方法接受一个里程值,并将其储存到 self.odometer_reading 中,在倒数第二行,调用了 update_odometer(),并向它提供了一个实参(该实参对应于方法定义中的形参mileage),它将里程数设置为66,而方法 read_odomter() 打印该读数:122018 Audi A9This car has 66 miles on it. 可对方法 update_odometer() 进行扩展,使其能够在修改里程表读数时做一些额外的工作,添加一些逻辑,禁止任何人将里程表读数往回调:1234567891011121314151617181920212223242526class Car(): def __init__(self,make,model,year): self.make = make self.model = model self.year = year self.odometer_reading = 50 def get_descriptive_name(self): long_name = str(self.year) + ' ' + self.make + ' ' +self.model return long_name.title() def read_odomter(self): print(\"This car has \" + str(self.odometer_reading) + \" miles on it.\") def update_odometer(self,mileage): if mileage >= self.odometer_reading: self.odometer_reading = mileage else: print(\"You can't roll back an odometer!\") my_new_car = Car('audi','a9','2018')print(my_new_car.get_descriptive_name())my_new_car.update_odometer(33)my_new_car.read_odomter() 修改 self.odometer_reading 的默认值为50,当我们再次尝试修改其值为33时,由于小于原来的里程,因此无法修改: 1232018 Audi A9You can't roll back an odometer!This car has 50 miles on it. 通过方法对属性的值进行递增:有时候需要将属性值递增到特定的量,而不是将其设置为全新的值,假设我们购买了一辆二手车,从购买到登记期间增加了100英里的里程,下面的方法让我们能够传递这个增量,并相应地增加里程表读数:123456789101112131415161718192021222324252627282930313233class Car(): def __init__(self,make,model,year): self.make = make self.model = model self.year = year self.odometer_reading = 0 def get_descriptive_name(self): long_name = str(self.year) + ' ' + self.make + ' ' +self.model return long_name.title() def read_odomter(self): print(\"This car has \" + str(self.odometer_reading) + \" miles on it.\") def update_odometer(self,mileage): if mileage >= self.odometer_reading: self.odometer_reading = mileage else: print(\"You can't roll back an odometer!\") def increment_odometer(self,miles): #将里程表读数增加指定的量 self.odometer_reading += miles my_new_car = Car('audi','a9','2018')print(my_new_car.get_descriptive_name())my_new_car.update_odometer(6600)my_new_car.read_odomter()my_new_car.increment_odometer(100)my_new_car.read_odomter() 输出结果如下:1232018 Audi A9This car has 6600 miles on it.This car has 6700 miles on it. - 8.4 继承 编写类时,并非总是要从空白开始,如果要编写的类是另一个现成类的特殊版本,可使用继承,一个类继承另一个类时,它自动获得另一个类的所有属性和方法;原有的类称为父类,而新类称为子类,子类继承了其父类的所有属性和方法,同时还可以定义自己的属性和方法;继承的通用语法大致如下: 12345678class ClassName1(object): def __init__(self,name1,name2,name3): --snip--class ClassName2(ClassName1): def __init__(self,name1,name2,name3): super().__init__(name1,name2,name3) --snip-- - 8.4.1 子类的方法init() 1234567891011121314151617181920 class Car(): def __init__(self,make,model,year): self.make = make self.model = model self.year = year self.odometer_reading = 0 def get_descriptive_name(self): long_name = str(self.year) + ' ' + self.make + ' ' +self.model return long_name.title()class ElectricCar(Car): #电动车的独特之处 def __init__(self,make,model,year): #初始化父类的属性 super().__init__(make,model,year)my_new_car = ElectricCar('tesla','model s','2016')print(my_new_car.get_descriptive_name()) 创建子类时,父类必须包含在当前文件中,且位于子类前面,定义了子类 ElectricCar,定义子类时,必须在括号内指定父类名称,方法 __init__()接受创建Car实例所需信息,super() 是一个特殊的函数,帮助Python将父类和子类关联起来,让Python调用 ElectricCar 的父类的方法 __init__(),让 ElectricCar 实例包含父类的所有属性,父类也称为超类(superclass),程序输出结果如下:12016 Tesla Model S - 8.4.2 Python 2.7 中的继承 在Python 2.7中,ElectricCar类的定义类似于下面这样:12345678class Car(object): def __init__(self,make,model,year): --snip--class ElectricCar(Car): def __init__(self,make,model,year): super(ElectricCar,self).__init__(make,model,year) --snip-- - 8.4.3 给子类定义属性和方法 让一个类继承另一个类后,可添加区分子类和父类所需的新属性和方法,下面添加一个电动车特有的属性(battery),以及一个描述该属性的方法: 1234567891011121314151617181920212223class Car(): def __init__(self,make,model,year): self.make = make self.model = model self.year = year self.odometer_reading = 0 def get_descriptive_name(self): long_name = str(self.year) + ' ' + self.make + ' ' +self.model return long_name.title()class ElectricCar(Car): def __init__(self,make,model,year): super().__init__(make,model,year) self.battery_size = 80 def describe_battery(self): print(\"This car has a \" + str(self.battery_size) + \"-KWh battery.\")my_new_car = ElectricCar('tesla','model s','2016')print(my_new_car.get_descriptive_name())my_new_car.describe_battery() 输出结果如下:122016 Tesla Model SThis car has a 80-KWh battery. - 8.4.4 重写父类的方法 要重写父类的方法,只需要在子类中定义一个与要重写的父类方法同名的方法即可,这样,Python将不会考虑这个父类的方法,而只关心在子类中定义的相应方法,假设Car类有一个名为 fill_gas_tank() 的方法,对于电动车来说毫无意义,因此可以重写它:12345class ElectricCar(Car): --snip-- def fill_gas_tank(self): print(\"This car doesn't need a gas tank!\") - 8.4.5 将实例用作属性 123456789101112131415161718192021222324252627282930class Car(): def __init__(self,make,model,year): self.make = make self.model = model self.year = year self.odometer_reading = 0 def get_descriptive_name(self): long_name = str(self.year) + ' ' + self.make + ' ' +self.model return long_name.title()class Battery(): #一次模拟电动车电瓶的简单尝试 def __init__(self,battery_size=70): #初始化电瓶的属性 self.battery_size = battery_size def describe_battery(self): #打印一条描述电瓶容量的消息 print(\"This car has a \" + str(self.battery_size) + \"-KWh battery.\")class ElectricCar(Car): def __init__(self,make,model,year): super().__init__(make,model,year) self.battery = Battery()my_new_car = ElectricCar('tesla','model s','2016')print(my_new_car.get_descriptive_name())my_new_car.battery.describe_battery() 输出结果如下: 122016 Tesla Model SThis car has a 70-KWh battery. 看起来似乎做了多余的工作,但现在我们可以对电瓶添加更多的描述,而且不会导致 ElectricCar 类混乱不堪,下面再给Battery添加一个方法,使其能够根据电瓶容量报告汽车的续航里程:1234567891011121314151617181920212223242526272829303132333435363738394041class Car(): def __init__(self,make,model,year): self.make = make self.model = model self.year = year self.odometer_reading = 0 def get_descriptive_name(self): long_name = str(self.year) + ' ' + self.make + ' ' +self.model return long_name.title()class Battery(): #一次模拟电动车电瓶的简单尝试 def __init__(self,battery_size=70): #初始化电瓶的属性 self.battery_size = battery_size def describe_battery(self): #打印一条描述电瓶容量的消息 print(\"This car has a \" + str(self.battery_size) + \"-KWh battery.\") def get_range(self): #打印一条消息,指出电瓶的续航里程 if self.battery_size == 70: range = 240 elif self.battery_size == 90: range = 280 message = \"This car can go approximately \" + str(range) message += \" miles on a full charge.\" print(message) class ElectricCar(Car): def __init__(self,make,model,year): super().__init__(make,model,year) self.battery = Battery()my_new_car = ElectricCar('tesla','model s','2016')print(my_new_car.get_descriptive_name())my_new_car.battery.describe_battery()my_new_car.battery.get_range() 输出结果如下:1232016 Tesla Model SThis car has a 70-KWh battery.This car can go approximately 240 miles on a full charge. - 8.5 导入类 Python允许将类储存在模块中,然后在主程序中导入所需的模块 - 8.5.1 导入单个类 12345678910111213141516171819202122232425262728293031#car.py#一个用于表示汽车的类class Car(): def __init__(self,make,model,year): #初始化描述汽车的属性 self.make = make self.model = model self.year = year self.odometer_reading = 0 def get_descriptive_name(self): #返回整洁的描述性名称 long_name = str(self.year) + ' ' + self.make + ' ' +self.model return long_name.title() def read_odomter(self): #打印一条消息,指出汽车的里程 print(\"This car has \" + str(self.odometer_reading) + \" miles on it.\") def update_odometer(self): #将里程表读数设置为指定的值,拒绝将里程表往回拨 if mileage >= self.odometer_reading: self.odometer_reading = mileage else: print(\"You can't roll back an odometer!\") def increment_odometer(self,miles): #将里程表读数增加指定的量 self.odometer_reading += miles 创建另一个文件——my_car.py,在其中导入Car类并创建其实例:123456789#my_car.pyfrom car import Carmy_new_car = Car('audi','a9','2018')print(my_new_car.get_descriptive_name())my_new_car.odometer_reading = 23my_new_car.read_odometer() import语句让Python打开模块car,并导入其中的Car类,输出结果如下: 122018 Audi A9This car has 23 miles on it. - 8.5.2 在一个模块中储存多个类 将类Battery和ElectricCar都加入到模块car.py中: 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758#car.py#一组用于表示燃油汽车和电动汽车的类class Car(): def __init__(self,make,model,year): #初始化描述汽车的属性 self.make = make self.model = model self.year = year self.odometer_reading = 0 def get_descriptive_name(self): #返回整洁的描述性名称 long_name = str(self.year) + ' ' + self.make + ' ' +self.model return long_name.title() def read_odometer(self): #打印一条消息,指出汽车的里程 print(\"This car has \" + str(self.odometer_reading) + \" miles on it.\") def update_odometer(self): #将里程表读数设置为指定的值,拒绝将里程表往回拨 if mileage >= self.odometer_reading: self.odometer_reading = mileage else: print(\"You can't roll back an odometer!\") def increment_odometer(self,miles): #将里程表读数增加指定的量 self.odometer_reading += milesclass Battery(): #一次模拟电动车电瓶的简单尝试 def __init__(self,battery_size=70): #初始化电瓶的属性 self.battery_size = battery_size def describe_battery(self): #打印一条描述电瓶容量的消息 print(\"This car has a \" + str(self.battery_size) + \"-KWh battery.\") def get_range(self): #打印一条消息,指出电瓶的续航里程 if self.battery_size == 70: range = 240 elif self.battery_size == 90: range = 280 message = \"This car can go approximately \" + str(range) message += \" miles on a full charge.\" print(message)class ElectricCar(Car): #模拟电动车的独特之处 def __init__(self,make,model,year): #初始化父类的属性,再初始化电动车特有的属性 super().__init__(make,model,year) self.battery = Battery() 新建一个my_electric_car.py的文件,导入ElectricCar类,并创建一辆电动车:123456789#my_electric_car.pyfrom car import ElectricCarmy_tesla = ElectricCar('tesla','model s','2016')print(my_tesla.get_descriptive_name())my_tesla.battery.describe_battery()my_tesla.battery.get_range() 输出结果如下:1232016 Tesla Model SThis car has a 70-KWh battery.This car can go approximately 240 miles on a full charge. - 8.5.3 从一个模块中导入多个类 可根据需要在程序文件中导入任意数量的类,假如我们要在同一个程序中创建普通汽车和电动汽车,就需要将类Car和ElectricCar类都导入,多个类之间用逗号进行分隔: 123456789 #my_car.pyfrom car import Car,ElectricCarmy_audi = Car('audi','a9','2018')print(my_audi.get_descriptive_name())my_tesla = ElectricCar('tesla','model s','2016')print(my_tesla.get_descriptive_name()) 输出结果如下:122018 Audi A92016 Tesla Model S - 8.5.4 导入整个模块 导入整个模块后,需要使用句点表示法访问需要的类:123456789#my_car.pyimport carmy_audi = car.Car('audi','a9','2018')print(my_audi.get_descriptive_name())my_tesla = car.ElectricCar('tesla','model s','2016')print(my_tesla.get_descriptive_name()) 我们导入了整个car模块,需要使用语法 module_name.class_name 访问需要的类,程序输出结果与8.5.3一致:122018 Audi A92016 Tesla Model S - 8.5.5 导入模块中的所有类 要导入模块中的所有类,可使用以下语法:1from module_name import * 这种导入方法是不推荐的,没有明确指出你使用了模块中的哪些类,还可能引发名称方面的困惑,需要从一个模块中导入很多类时,最好导入整个模块,并使用 module_name.class_name 语法来访问类 - 8.5.6 在一个模块中导入另一个模块 有时候需要将类分散到多个模块当中,以免模块太大,或者在同一个模块中储存不相关的类,将类储存在多个模块中时,一个模块中的类可能会依赖于另一个模块中的类,这种情况下,我们可以在前一个模块中导入必要的类,以下例子中,将Car类储存在一个模块当中,并将ElectricCar和Battery类储存在另一个模块当中,将第二个模块命名为electric_car.py,并将ElectricCar和Battery类复制到这个模块中:12345678910111213141516171819202122232425262728293031#electric_car.py#一组可用于表示电动汽车的类from car import Carclass Battery(): #一次模拟电动车电瓶的简单尝试 def __init__(self,battery_size=70): #初始化电瓶的属性 self.battery_size = battery_size def describe_battery(self): #打印一条描述电瓶容量的消息 print(\"This car has a \" + str(self.battery_size) + \"-KWh battery.\") def get_range(self): #打印一条消息,指出电瓶的续航里程 if self.battery_size == 70: range = 240 elif self.battery_size == 90: range = 280 message = \"This car can go approximately \" + str(range) message += \" miles on a full charge.\" print(message)class ElectricCar(Car): #模拟电动车的独特之处 def __init__(self,make,model,year): #初始化父类的属性,再初始化电动车特有的属性 super().__init__(make,model,year) self.battery = Battery() 12345678910111213141516171819202122232425262728293031#car.py#一个可用于表示汽车的类class Car(): def __init__(self,make,model,year): #初始化描述汽车的属性 self.make = make self.model = model self.year = year self.odometer_reading = 0 def get_descriptive_name(self): #返回整洁的描述性名称 long_name = str(self.year) + ' ' + self.make + ' ' +self.model return long_name.title() def read_odometer(self): #打印一条消息,指出汽车的里程 print(\"This car has \" + str(self.odometer_reading) + \" miles on it.\") def update_odometer(self): #将里程表读数设置为指定的值,拒绝将里程表往回拨 if mileage >= self.odometer_reading: self.odometer_reading = mileage else: print(\"You can't roll back an odometer!\") def increment_odometer(self,miles): #将里程表读数增加指定的量 self.odometer_reading += miles 现在可以分别从每个模块中导入类:12345678910#my_car.pyfrom car import Carfrom electric_car import ElectricCarmy_audi = Car('audi','a9','2018')print(my_audi.get_descriptive_name())my_tesla = ElectricCar('tesla','model s','2016')print(my_tesla.get_descriptive_name()) 输出结果如下:122018 Audi A92016 Tesla Model S - 8.6 Python标准库 Python标准库是一组模块,安装的Python都包含它,我们可以使用标准库中的任何函数和类,只需要在程序的开头包含一条简单的import语句,下面以模块collections中的一个类——OrderedDict(创建字典并记录其中的键-值对的添加顺序)为例:1234567891011121314#favorite_languages.pyfrom collections import OrderedDictfavorite_languages = OrderedDict()favorite_languages ['jen'] = 'python'favorite_languages ['sarah'] = 'c'favorite_languages ['edward'] = 'java'favorite_languages ['anly'] = 'python'for name,language in favorite_languages.items(): print(name.title() + \"'s favorite languages is \" + language.title() + \".\") 输出结果如下:1234Jen's favorite languages is Python.Sarah's favorite languages is C.Edward's favorite languages is Java.Anly's favorite languages is Python.","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"基础学习","slug":"Python3-学习笔记/基础学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/基础学习/"}],"tags":[{"name":"类","slug":"类","permalink":"https://www.itrhx.com/tags/类/"},{"name":"继承","slug":"继承","permalink":"https://www.itrhx.com/tags/继承/"}]},{"title":"Python3 基础学习笔记 C07","slug":"A13-Python3-basic-C07","date":"2018-11-03T14:21:47.735Z","updated":"2019-09-24T12:45:42.627Z","comments":true,"path":"2018/11/03/A13-Python3-basic-C07/","link":"","permalink":"https://www.itrhx.com/2018/11/03/A13-Python3-basic-C07/","excerpt":"Python3 基础学习笔记第七章 —— 【函数】","text":"Python3 基础学习笔记第七章 —— 【函数】 - 7.1 定义函数 一个简单的函数,命名为 example(),其中,关键字 def 来告诉Python我们要定义一个函数,这就是函数定义 123def example(): print(\"Hello world!\")example() 输出结果如下: 1Hello world! - 7.1.1 向函数传递信息 在函数定义 def example() 的括号中添加 username,可以让函数接受我们给 username 指定的任何值,在调用函数时给 username 指定一个值,调用 example() 时,可将一个名字传递给它: 123def example(username): print(\"Hello , \" + username + '!')example('TRHX') 输出结果如下: 1Hello , TRHX! - 7.1.2 实参和形参 在 7.1.1 的例子中,函数 example() 的定义中,变量 username 是一个形参——函数完成其工作所需的一项信息,在代码 example(‘TRHX’) 中,值’TRHX’是一个实参,实参是调用函数时传递给函数的信息,调用函数时,将要让函数使用的信息放在括号内。在 example(‘TRHX’) 中,将实参 ‘TRHX’ 传递给了函数 example,这个值被储存在形参 username 中 - 7.2 传递实参 鉴于函数定义中可能包含多个形参,因此函数调用中也可能包含多个实参。向函数传递实参的方式很多,可使用位置实参,这要求实参的顺序与形参的顺序相同;也可以使用关键字实参,其中每个实参都由变量和值组成;还可以使用列表和字典 - 7.2.1 位置实参 调用函数时,Python必须将函数调用中的每个实参都关联到函数定义中的一个形参。为此,最简单的方法是基于实参的顺序,这种关联方式被称为位置实参 1234def describe_pet(animal_type , pet_name): print(\"I have a \" + animal_type + \".\") print(\"My \" + animal_type + \"'s name is \" + pet_name.title() + \".\")describe_pet('hamster' , 'harry') 输出结果如下: 12I have a hamster.My hamster's name is Harry. 调用函数多次:我们可以根据需要调用函数任意次,要再描述一个宠物,只需要再次调用 123456describe_pet() 即可def describe_pet(animal_type , pet_name): print(\"I have a \" + animal_type + \".\") print(\"My \" + animal_type + \"'s name is \" + pet_name.title() + \".\")describe_pet('hamster' , 'harry')describe_pet('dog' , 'willi') 输出结果如下: 1234I have a hamster.Myhamster's name is Harry.I have a dog.My dog's name is Willi. - 7.2.2 关键字实参 关键字实参是传递给函数的名称-值对。直接在实参中将名称和值关联起来,不用考虑函数调用中的实参顺序 12345def describe_pet(animal_type , pet_name): print(\"I have a \" + animal_type + \".\") print(\"My \" + animal_type + \"'s name is \" + pet_name.title() + \".\")describe_pet(animal_type = 'hamster' , pet_name = 'harry')describe_pet(pet_name = 'willi' , animal_type = 'dog' ) 输出结果如下: 1234I have a hamster.Myhamster's name is Harry.I have a dog.My dog's name is Willi. - 7.2.3 默认值 编写函数时,可给每个形参指定默认值,在调用函数中给形参提供了实参时,Python将使用指定的实参值,否则将使用形参的默认值 1234def describe_pet(pet_name , animal_type = 'dog'): print(\"I have a \" + animal_type + \".\") print(\"My \" + animal_type + \"'s name is \" + pet_name.title() + \".\")describe_pet(pet_name = 'willi') 输出结果如下: 12I have a dog.My dog's name is Willi. 在这个函数定义中,修改了形参的排列顺序,由于给 animal_type 指定了默认值,无需通过实参来指定动物类型,因此在函数调用中只包含一个实参——宠物的名字,然而Python依然将这个实参视为位置实参,因此如果函数调用中只包含宠物的名字,这个实参将关联到函数定义中的第一个形参,这就是需要将 pet_name 放在形参列表开头的原因所在 注意:使用默认值时,在形参列表中必须先列出没有默认值的形参,再列出有默认值的形参,这让Python依然能够准确地解读位置实参 - 7.3 返回值 函数并非总是直接显示输出,相反,它可以处理一些数据,并返回一个或一组值,函数返回的值被称为返回值,在函数中,可使用 return 语句将值返回到函数调用的代码行 - 7.3.1 返回简单值 12345def name(first_name , last_name): full_name = first_name + ' ' + last_name return full_name.title()student = name('jimi' , 'hendrix')print(student) 输出结果如下: 1Jimi Hendrix - 7.3.2 让实参变成可选的 对 7.3.1 的例子进行改进,扩展函数 name,使其还能够处理中间名: 12345def name(first_name , middle_name , last_name): full_name = first_name + ' ' + middle_name + ' ' + last_name return full_name.title()student = name('jimi' , 'lee' , 'hendrix')print(student) 输出结果如下: 1Jimi Lee Hendrix 然而,如果一个人没有中间名,那么在调用这个函数时就会出错,为了让中间名变成可选的,可以给实参 middle_name 指定一个默认值——空字符串,并在用户没有提供中间名时不使用这个实参,注意需要将 middle_name 移到形参列表的末尾: 12345678910def name(first_name , last_name , middle_name = ' '): if middle_name: full_name = first_name + ' ' + middle_name + ' ' + last_name else: full_name = first_name + ' ' + last_name return full_name.title()student = name('jimi' , 'hendrix')print(student)student = name('jimi' , 'hendrix' , 'lee' )print(student) 输出结果如下: 12Jimi HendrixJimi Lee Hendrix - 7.3.3 返回字典 函数可返回任何类型的值,包括列表和字典等较复杂的数据结构: 12345def name(first_name , last_name): full_name = {'first' : first_name , 'last' : last_name} return full_namestudent = name('jimi' , 'hendrix')print(student) 输出结果如下: 1{'first': 'jimi', 'last': 'hendrix'} - 7.3.4 结合使用函数和 while 循环 123456789101112131415def name(first_name , last_name): full_name = first_name + ' ' + last_name return full_namewhile True: print(\"\\nPlease input your name:\") print(\"(Enter 'exit' to quit)\") f_name = input(\"First_name:\") if f_name == 'exit': break l_name = input(\"Last_name:\") if l_name == 'exit': break student = name(f_name , l_name) print(student) print(\"Hello, \" + student.title() + \"!\") 运行程序: 1234567891011Please input your name:(Enter 'exit' to quit)First_name:jimiLast_name:hendrixjimi hendrixHello, Jimi Hendrix!Please input your name:(Enter 'exit' to quit)First_name:exit - 7.4 传递列表 123456def users(names): for name in names: message = \"Hello, \" + name.title() + \"!\" print(message)usernames = ['hannah' , 'tony' , 'margot']users(usernames) 输出结果如下: 123Hello, Hannah!Hello, Tony!Hello, Margot! - 7.4.1 在函数中修改列表 将列表传递给函数后,函数就可以对其进行修改,在函数中对这个列表所做的任何修改都是永久性的 #首先创造一个列表,其中包含一些要打印的设计 12345678910111213141516unprinted_designs = ['iphone case' , 'robot pendannt' , 'dodecahedron']completed_models = []#模拟打印每个设计,直到没有未打印的设计为止#打印每个设计后,都将其移到列表completed_models中while unprinted_designs: current_design = unprinted_designs.pop() #模拟根据设计制作3D打印模型的过程 print(\"Printing model: \" + current_design) completed_models.append(current_design) #显示打印好的所有模型print(\"\\nThe following models have been printed: \")for completed_model in completed_models: print(completed_model) 输出结果如下: 12345678Printing model: dodecahedronPrinting model: robot pendanntPrinting model: iphone caseThe following models have been printed: dodecahedronrobot pendanntiphone case 编写两个函数重新组织这些代码,每一个函数都做一件具体的工作,输出结果与原程序相同: 123456789101112131415161718192021def print_models(unprinted_designs , completed_models):#模拟打印每个设计,直到没有未打印的设计为止#打印每个设计后,都将其移到列表completed_models中 while unprinted_designs: current_design = unprinted_designs.pop() #模拟根据设计制作3D打印模型的过程 print(\"Printing model: \" + current_design) completed_models.append(current_design)def show_completed_models(completed_models): #显示打印好的所有模型 print(\"\\nThe following models have been printed: \") for completed_model in completed_models: print(completed_model)unprinted_designs = ['iphone case' , 'robot pendannt' , 'dodecahedron']completed_models = []print_models(unprinted_designs , completed_models)show_completed_models(completed_models) - 7.4.2 禁止函数修改列表 有时候需要禁止函数修改列表,拿 7.4.1 的例子来说,我们打印了所有设计后,也要保留原来的未打印的设计列表,以供备案,但由于我们将所有的设计都移出了 unprinted_designs,这个列表变成了空的,原来的列表没有了,为了解决这个问题,可向函数传递列表的副本而不是原件;这样函数所做的任何修改都只影响副本,而丝毫不影响原件,要将列表的副本传递给函数,可以像下面这样做: 1function_name(list_name[:]) 切片表示法 [:] 创建列表的副本,在 7.4.1 的例子中如果不想清空未打印的设计列表,可像下面这样调用 print_models(): 1print_models(unprinted_designs[:] , completed_models) - 7.5 传递任意数量的实参 Python允许函数从调用语句中收集任意数量的实参 1234def make_pizza(*toppings): print(toppings)make_pizza('pepperoni')make_pizza('mushrooms' , 'green peppers' , 'extra cheese') 形参名 *toppings 中的星号让Python创建一个名为 toppings 的空元组,并将收到的所有值都封装到这个元组中,函数体内的print语句通过生成输出来证明Python能够处理使用一个值调用函数的情形,也能处理使用三个值来调用函数的情形,输出结果如下: 12('pepperoni',)('mushrooms', 'green peppers', 'extra cheese') 使用循环语句: 123456def make_pizza(*toppings): print(\"\\nMaking a pizza with the followiing toppings: \") for topping in toppings: print(\"- \" + topping)make_pizza('pepperoni')make_pizza('mushrooms' , 'green peppers' , 'extra cheese') 输出结果如下: 12345678Making a pizza with the followiing toppings: - pepperoniMaking a pizza with the followiing toppings: - mushrooms- green peppers- extra cheese - 7.5.1 结合使用位置实参和任意数量实参 如果要让函数接受不同类型的实参,必须在函数定义中将接纳任意数量实参的形参放在最后。Python先匹配位置实参和关键字实参,再将余下的实参都收集到最后一个形参中: 123456def make_pizza(size , *toppings): print(\"\\nMaking a \" + str(size) + \"-inch pizza with the followiing toppings: \") for topping in toppings: print(\"- \" + topping)make_pizza(16 , 'pepperoni')make_pizza(18 , 'mushrooms' , 'green peppers' , 'extra cheese') 输出结果如下: 12345678Making a 16-inch pizza with the followiing toppings: - pepperoniMaking a 18-inch pizza with the followiing toppings: - mushrooms- green peppers- extra cheese - 7.5.2 使用任意数量的关键字实参 有时候,需要接受任何数量的实参,但预先我们不知道传递给函数的会是什么样的信息,在这种情况下,可以将函数编写成能够接受任意数量的键-值对——调用语句提供了多少就接受多少: 12345678910def build_profile(first , last , **user_info): #创建一个字典,其中包括我们知道的有关用户的一切 profile = {} profile['first_name'] = first profile['last_name'] = last for key , value in user_info.items(): profile[key] = value return profileuser_profile = build_profile('albert' , 'einstein' , location = 'princeton' , field = 'physics')print(user_profile) 形参 **user_info 中的两个星号让Python创建一个名为 user_info 的空字典,并将收到的所有名称-值对都封装到这个字典中,在这个函数中,可以像访问其他字典那样访问 user_info 中的名字-值对,程序运行结果如下: 1{'first_name': 'albert', 'last_name': 'einstein', 'location': 'princeton', 'field': 'physics'} - 7.6 将函数储存在模块中 更进一步,我们可以把函数储存在被称为模块的独立文件中,再将模块导入到主程序中,import 语句运行在当前运行的程序文件中使用模块中的代码 - 7.6.1 导入整个模块 要让函数是可导入的,得先创建模块,模块是扩展名为.py的文件,包含要导入到程序中的代码,下面将创建一个包含函数 make_pizza() 的模块 1234567#pizza.pydef make_pizza(size , *toppings): #概述要制作的比萨 print(\"\\nMaking a \" + str(size) + \"-inch pizza with the followiing toppings: \") for topping in toppings: print(\"- \" + topping) 接下来,我们在 pizza.py 所在的目录中创建另一个名为 making_pizzas.py 的文件,在这个文件中导入刚刚创建的模块,在调用 make_pizza() 两次: 12345#making_pizzas.pyimport pizzapizza.make_pizza(16 , 'pepperoni')pizza.make_pizza(18 , 'mushrooms' , 'green peppers' , 'extra cheese') Python在读取这个文件时,代码行 import pizza 让Python打开文件 pizza.py,并在幕后将其中所有函数都复制到这个程序中,在 making_pizzas.py 中,可以使用 pizza.py 中定义的所有函数,要调用被导入的模块中的函数,可指定导入的模块的名称 pizza 和函数名 make_pizza(),并使用句点分隔它们,最终运行结果与原程序相同: 12345678Making a 16-inch pizza with the followiing toppings: - pepperoniMaking a 18-inch pizza with the followiing toppings: - mushrooms- green peppers- extra cheese - 7.6.2 导入特定的函数 导入模块中特定的函数,可以使用以下语法: 1from module_name import function_name 通过用逗号分隔函数名,可根据需要从模块中导入任意数量的函数:1from module_name import function_0 , function_1 , function_2 以前面的 making_pizzas.py 为例,如果只想导入要使用的函数,代码类似于下面这样:1234from pizza import make_pizzamake_pizza(16 , 'pepperoni')make_pizza(18 , 'mushrooms' , 'green peppers' , 'extra cheese') - 7.6.3 使用 as 给函数指定别名 如果要导入的函数名称可能与程序中现有的名称冲突,或者函数的名称太长,可指定简短而独一无二的别名,要给函数指定别名,需要在导入它的时候这样做,通用语法为:1from module_name import function_name as fn 同样以前面的 making_pizzas.py 为例:1234from pizza import make_pizza as mpmp(16 , 'pepperoni')mp(18 , 'mushrooms' , 'green peppers' , 'extra cheese') - 7.6.4 使用 as 给模块指定别名 我们还可以给模块指定别名,通用语法为:1import module_name as mn 同样以前面的 making_pizzas.py 为例:1234import pizza as pp.make_pizza(16 , 'pepperoni')p.make_pizza(18 , 'mushrooms' , 'green peppers' , 'extra cheese') - 7.6.5 导入模块中的所有函数 导入模块中所有函数的通用语法为:1from module_name import * 同样以前面的 making_pizzas.py 为例:1234from pizza import *make_pizza(16 , 'pepperoni')make_pizza(18 , 'mushrooms' , 'green peppers' , 'extra cheese') import 语句中的星号让Python将模块 pizza 中的每个函数都复制到这个程序中,由于导入了每个函数,可通过名称来调用每个函数,而不需要用句点表示法,然而,如果模块中有函数的名称与项目中的名称相同,就有可能导致意想不到的结果,最佳的做法是,要么只导入我们需要使用的函数,要么导入整个模块并使用句点表示法","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"基础学习","slug":"Python3-学习笔记/基础学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/基础学习/"}],"tags":[{"name":"函数","slug":"函数","permalink":"https://www.itrhx.com/tags/函数/"},{"name":"模块","slug":"模块","permalink":"https://www.itrhx.com/tags/模块/"}]},{"title":"Python3 基础学习笔记 C06","slug":"A12-Python3-basic-C06","date":"2018-10-30T05:42:31.562Z","updated":"2019-09-24T12:45:39.386Z","comments":true,"path":"2018/10/30/A12-Python3-basic-C06/","link":"","permalink":"https://www.itrhx.com/2018/10/30/A12-Python3-basic-C06/","excerpt":"Python3 基础学习笔记第六章 —— 【用户输入和 while 循环】","text":"Python3 基础学习笔记第六章 —— 【用户输入和 while 循环】 - 6.1 函数 input() 的工作原理 函数 input() 让程序暂停运行,等待用户输入一些文本。获取用户输入后,Python将其储存在一个变量当中,以方便你使用;函数 input() 返回为 string 类型 12message = input(\"Please tell me your name:\")print(\"Hello , \" + message + \"!\") 输出结果如下: 12Please tell me your name:anliyHello , anliy! 进阶: 1234message = \"Please tell me your name so that we can personalize the messages you see.\"message += \"\\nWhat's your first name?\"name = input(message)print(\"\\nHello , \" + name + \"!\") 输出结果如下: 1234Please tell me your name so that we can personalize the messages you see.What's your first name?trhxHello , trhx! - 6.1.1 使用 int() 来获取数值输入 使用函数 input() 时,Python会将用户输入解读为字符串: 1234>>> age = input(\"How old are you?\")How old are you?19>>> age'19' 为了解决这个问题,可以使用函数 int() ,它让Python将输入视为数值: 12345>>> age = input(\"How old are you?\")How old are you?19>>> age = int(age)>>> age19 实例: 123456age = input(\"Please tell me your age:\")age = int(age)if age >= 18: print(\"You are old enough to go to the Internet bar!\")else: print(\"You are not old enough to go to Internet bar!\") 输出结果如下: 12Please tell me your age:17You are not old enough to go to Internet bar! - 6.1.2 求模运算符 处理数值信息时,求模运算符(%)是一个很有用的工具,它将两个数相除并返回余数: 12345678>>> 4 % 31>>> 5 % 32>>> 8 % 20>>> 7 % 31 - 6.1.3 在 Python 2.7 中获取输入 如果使用 Python 2.7,应该使用函数 raw_input() 来提示用户输入,这个函数与 Python 3 中的 input() 一样,也将输入解读为字符串;Python 2.7 也包含函数 input(),但它将用户输入解读为Python代码,并尝试运行它们 - 6.2 while 循环 for 循环用于针对集合中的每一个元素的一个代码块,而 while 循环不断地运行,直到指定的条件不满足为止 - 6.2.1 使用 while 循环 一个简单的 while 循环: 1234num = 1while num < 5: print(num) num += 1 输出结果如下: 12341234 - 6.2.2 让用户选择退出循环 123456prompt = \"\\nTell me something, and I will repeat it back to you:\"prompt += \"\\nEnter 'quit' to end the program.\"message = \" \"while message != 'quit': message = input(prompt) print(message) 运行程序: 123456789101112Tell me something, and I will repeat it back to you:Enter 'quit' to end the program.Hello everyone!Hello everyone!Tell me something, and I will repeat it back to you:Enter 'quit' to end the program.Hello again!Hello again!Tell me something, and I will repeat it back to you:Enter 'quit' to end the program.quitquit - 6.2.3 使用标志 在要求很多条件都满足才继续运行的程序中,可以定义一个变量,用于判断整个程序是否处于活动状态,这个变量称为标志 123456789prompt = \"\\nTell me something, and I will repeat it back to you:\"prompt += \"\\nEnter 'quit' to end the program.\"active = Truewhile active: message = input(prompt) if message == 'quit': active = False else: print(message) 运行结果与6.2.2一致 - 6.2.4 使用 break 退出循环 要立即退出 while 循环,不再运行循环中余下的代码,也不管条件测试的结果如何,可使用 break 语句,break 语句用于控制程序流程,可使用它来控制哪些代码将执行,哪些代码不执行 123456789prompt = \"\\nPlease enter the name of a city you have visited:\"prompt += \"\\nEnter 'quit' when you are finished.\"active = Truewhile active: city = input(prompt) if city == 'quit': break else: print(\"I'd love to go to \" + city.title() + \"!\") 运行程序: 1234567891011Please enter the name of a city you have visited:Enter 'quit' when you are finished.ShanghaiI'd love to go to Shanghai!Please enter the name of a city you have visited:Enter 'quit' when you are finished.BeijingI'd love to go to Beijing!Please enter the name of a city you have visited:Enter 'quit' when you are finished.quit 在任何Python循环中都可以使用break语句,例如,可以使用break语句来退出遍历列表或字典 - 6.2.5 在循环中使用 continue 要返回到循环开头,并根据条件测试结果决定是否继续执行循环,可使用 continue 语句,它不像 break 语句那样不再执行余下的代码并退出整个循环,例如,从1到10只打印其中奇数: 123456number =0while number < 10: number += 1 if number % 2 == 0: continue print(number) 输出结果如下:1234513579 - 6.3 使用 while 循环来处理列表和字典 for循环是一种遍历列表的有效方式,但在for循环中不应修改列表,否则将导致Python难以跟踪其中的元素,要在遍历列表的同时对其进行修改,可使用while循环 - 6.3.1 在列表之间移动元素 123456789unconfirmed_users = ['alice' , 'brian' , 'candace']confirmed_users = []while unconfirmed_users: current_user = unconfirmed_users.pop() print(\"Verifying user: \" + current_user.title()) confirmed_users.append(current_user)print(\"\\nThe following users have been confirmed:\")for confirmed_user in confirmed_users: print(confirmed_user.title()) 首先创建一个未验证用户列表,其中包含用户Alice、Brian和Candace,还创建了一个空列表,用于存储已验证的用户,程序中的 while 循环将不断地运行,直到列表 unconfirmed_users 变成空的。在这个循环中,函数pop() 以每次一个的方式从列表 unconfirmed_users 末尾删除未验证的用户。由于Candace位于列表 unconfirmed_users 的末尾,因此其名字将首先被删除、存储到变量 current_user 中并加入到列表 confirmed_users 中。接下来是Brian,然后是Alice 为模拟用户验证过程,我们打印一条验证消息并将用户加入到已验证用户列表中。未验证用户列表越来越短,而已验证用户列表越来越长。未验证用户列表为空后结束循环,再打印已验证用户列表: 12345678Verifying user: CandaceVerifying user: BrianVerifying user: AliceThe following users have been confirmed:CandaceBrianAlice - 6.3.2 删除包含特定值的所有列表元素 可以使用方法 remove() 来删除列表中特定的值,但如果要删除的值在列表中出现了多次,方法 remove() 就不管用了,如果要删除列表中所有包含特定值的元素则可以使用 while 循环: 12345names = ['alice' , 'candace' , 'alice' , 'brian' , 'alix' , 'candace' , 'heliy']print(names)while 'candace' in names: names.remove('candace')print(names) 输出结果如下: 12['alice', 'candace', 'alice', 'brian', 'alix', 'candace', 'heliy']['alice', 'alice', 'brian', 'alix', 'heliy'] 使用方法 remove() 做对比: 1234names = ['alice' , 'candace' , 'alice' , 'brian' , 'alix' , 'candace' , 'heliy']print(names)names.remove('candace')print(names) 输出结果如下: 12['alice', 'candace', 'alice', 'brian', 'alix', 'candace', 'heliy']['alice', 'alice', 'brian', 'alix', 'candace', 'heliy'] - 6.3.3 使用用户输入来填充字典 12345678910111213141516171819202122responses = {}#设置一个标志,指出调查是否继续polling_active = Truewhile polling_active: #提示输入被调查者的姓名和回答 name = input(\"\\nWhat's your name?\") response = input(\"What kind of fruit do you like?\") #将答卷储存在字典中 responses[name] = response #询问是否还有其他人要参与回答 repeat = input(\"Would you like to let another person respond?(Yes/No)\") if repeat == 'No': polling_active = False#调查结束,显示结果print(\"\\n------ Poll Results ------\")for name , response in responses.items(): print(name + \" like \" + response + \".\") 运行程序: 1234567891011What's your name?TRHXWhat kind of fruit do you like?appleWould you like to let another person respond?(Yes/No)YesWhat's your name?TRHXCCWhat kind of fruit do you like?bananaWould you like to let another person respond?(Yes/No)No------ Poll Results ------TRHX like apple.TRHXCC like banana.","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"基础学习","slug":"Python3-学习笔记/基础学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/基础学习/"}],"tags":[{"name":"input()函数","slug":"input-函数","permalink":"https://www.itrhx.com/tags/input-函数/"},{"name":"while循环","slug":"while循环","permalink":"https://www.itrhx.com/tags/while循环/"}]},{"title":"Python3 基础学习笔记 C05","slug":"A11-Python3-basic-C05","date":"2018-10-27T11:10:10.825Z","updated":"2019-09-24T12:45:35.909Z","comments":true,"path":"2018/10/27/A11-Python3-basic-C05/","link":"","permalink":"https://www.itrhx.com/2018/10/27/A11-Python3-basic-C05/","excerpt":"Python3 基础学习笔记第五章 —— 【字典】","text":"Python3 基础学习笔记第五章 —— 【字典】 - 5.1 一个简单的字典 123fruits = {'apple' : 'red' , 'number' : 5}print(fruits['apple'])print(fruits['number']) 输出结果如下: 12red5 在Python中,字典是一系列键-值对。每个键都与一个值相关联,你可以使用键来访问与之相关联的值。与键相关联的值可以是数字、字符串、列表乃至字典。事实上,可以将任何Python对象用作字典中的值。键-值对是两个相关联的值。在指定键时,Python将返回与之相关联的值。键和值之间用冒号分隔,而键-值对之间用逗号分隔。在字典中,想储存多少个键-值对都可以 - 5.1.1 访问字典中的值 要获取与键相关联的值,可依次指定字典名和放在方括号内的键: 123fruits = {'apple' : 'red' , 'number' : 5}number_fruits = fruits['number']print(\"The number of apple is \" + str(number_fruits) + \"!\") 输出结果如下: 1The number of apple is 5! - 5.1.2 添加键-值对 字典是一种动态结构,可随时在其中添加键-值对。要添加键-值对,可依次指定字典名、用方括号括起来的键和相关联的值 12345fruits = {'apple' : 'red' , 'number1' : 5}print(fruits)fruits['banana'] = 'yellow'fruits['number2'] = 13print(fruits) 输出结果如下: 12{'apple': 'red', 'number1': 5}{'apple': 'red', 'number1': 5, 'banana': 'yellow', 'number2': 13} 注意:键-值对的排列顺序与添加顺序不同。Python不关心键-值对的添加顺序,而只关心键和值之间的关联关系 有时候为了方便也可以先使用一对空的花括号定义一个字典,再分行添加各个键-值对: 1234fruits = {}fruits['banana'] = 'yellow'fruits['number2'] = 13print(fruits) 输出结果如下: 1{'banana': 'yellow', 'number2': 13} - 5.1.3 修改字典中的值 要修改字典中的值,可依次指定字典名、用方括号括起来的键以及与该键相关联的新值 1234fruits = {'color' : 'red'}print(\"The color of the fruits is \" + fruits['color'] + \"!\")fruits['color'] = 'yellow'print(\"The color of the fruits is \" + fruits['color'] + \" now!\") 输出结果如下: 12The color of the fruits is red!The color of the fruits is yellow now! 进阶:对一个能够以不同速度移动的外星人的位置进行跟踪,为此,我们将储存该外星人的当前速度,并据此确定该外星人将向右移动多远: 1234567891011121314alien = {'x_position': 0, 'y_position': 25, 'speed': 'medium'}print(\"Original x-position: \" + str(alien['x_position']))#向右移动外星人,据外星人当前速度决定将其移动多远if alien['speed'] == 'slow': x_increment = 1elif alien['speed'] == 'medium': x_increment = 2else: x_increment = 3#新位置等于老位置加上增量alien['x_position'] = alien['x_position'] + x_incrementprint(\"New x_position: \" + str(alien['x_position'])) 输出结果如下: 12Original x-position: 0New x_position: 2 - 5.1.4 删除键-值对 对于字典中不再需要的信息,可使用del语句将相应的键-值对彻底删除。使用del语句时,必须指定字典名和要删除的键 1234fruits = {'apple' : 'red' , 'number' : 5}print(fruits)del fruits['number']print(fruits) 输出结果如下: 12{'apple': 'red', 'number': 5}{'apple': 'red'} - 5.1.5 由类似对象组成的字典 字典储存的可以是一个对象的多种信息,也可以储存众多对象的同一种信息,例如要调查很多人最喜欢的编程语言: 1234567favorite_languages = { 'jen' : 'python' , 'sarah' : 'c' , 'edward' : 'ruby' , 'phil' : 'java' , }print(\"Sarah's favorite languages is \" + favorite_languages['sarah'].title() + \"!\") 输出结果如下: 1Sarah's favorite languages is C! - 5.2 遍历字典 - 5.2.1 方法 items() 遍历所有的键-值对 使用for循环来遍历字典:12345678name = { 'username' : 'efermi' , 'first' : 'enrico' , 'last' : 'fermi' , }for key , value in name.items(): print(\"\\nKey: \" + key) print(\"Value: \" + value) 输出结果如下:123456789Key: usernameValue: efermiKey: firstValue: enricoKey: lastValue: fermi for语句的第二部分包含字典和方法items(),它返回一个键-值对列表。接下来,for循环依次将每个键-值对储存到指定的两个变量中 12345678favorite_languages = { 'jen' : 'python' , 'sarah' : 'c' , 'edward' : 'ruby' , 'phil' : 'java' , }for name, language in favorite_languages.items(): print(name.title() + \"'s favorite language is \" + language.title() + \".\") 输出结果如下: 1234Jen's favorite language is Python.Sarah's favorite language is C.Edward's favorite language is Ruby.Phil's favorite language is Java. - 5.2.2 方法 keys() 遍历字典中所有的键 在不需要使用字典中的值时,方法key()很有用,下面来遍历字典favorite_languages,并将每个被调查者的名字都打印出来: 12345678favorite_languages = { 'jen' : 'python' , 'sarah' : 'c' , 'edward' : 'ruby' , 'phil' : 'java' , }for name in favorite_languages.keys(): print(name.title()) 输出结果如下: 1234JenSarahEdwardPhil 遍历字典时,会默认遍历所有的键,因此,如果将上述代码中的for name in favorite_languages.keys():替换为for name in favorite_languages:输出结果将不变进阶: 1234567891011favorite_languages = { 'jen' : 'python' , 'sarah' : 'c' , 'edward' : 'ruby' , 'phil' : 'java' , }friends = ['phil', 'sarah']for name in favorite_languages.keys(): print(name.title()) if name in friends: print(\"Hi \" + name + \", I see your favorite languages is \" + favorite_languages[name].title() + \"!\") 输出结果如下: 123456JenSarahHi sarah, I see your favorite languages is C!EdwardPhilHi phil, I see your favorite languages is Java! - 5.2.3 函数 sorted() 按顺序遍历字典中的所有键 字典总是明确地记录键和值之间的关联关系,但获取字典的元素时,获取顺序是不可预测的,要以特定的顺序返回元素,一种办法是在for循环中对返回的键进行排序,为此,可以使用函数sorted()来获得按特定顺序排列的键列表的副本: 12345678favorite_languages = { 'jen' : 'python' , 'sarah' : 'c' , 'edward' : 'ruby' , 'phil' : 'java' , }for name in sorted(favorite_languages.keys()): print(name.title()) 输出结果如下: 1234EdwardJenPhilSarah - 5.2.4 方法 values() 遍历字典中的所有值 12345678favorite_languages = { 'jen' : 'python' , 'sarah' : 'c' , 'edward' : 'ruby' , 'phil' : 'java' , }for languages in favorite_languages.values(): print(languages.title()) 输出结果如下: 1234PythonCRubyJava 这种做法提取字典中所有的值,而没有考虑是否重复,为剔除重复项,可使用集合(set),集合类似于列表,但每个元素都必须是独一无二的: 12345678favorite_languages = { 'jen' : 'python' , 'sarah' : 'c' , 'edward' : 'ruby' , 'phil' : 'python' , }for languages in set(favorite_languages.values()): print(languages.title()) 输出结果如下: 123CPythonRuby - 5.3 嵌套 有时候,需要将一系列字典储存在列表中,或将列表作为值储存在字典中,这称为嵌套。可以在列表中嵌套字典、在字典中嵌套列表甚至在字典中嵌套字典 - 5.3.1 字典列表 下面代码创建三个字典,每个字典都表示一个个学生,将这三个字典都放到一个名为students的列表当中,遍历列表将每个学生都打印出来: 123456student_0 = {'name' : 'anily' , 'class' : 2}student_1 = {'name' : 'nikey' , 'class' : 5}student_2 = {'name' : 'heyk' , 'class' : 3}students = [student_0 , student_1 , student_2]for student in students: print(student) 输出结果如下: 123{'name': 'anily', 'class': 2}{'name': 'nikey', 'class': 5}{'name': 'heyk', 'class': 3} 进阶:使用 range() 自动生成三十个外星人: 123456789101112131415#创建一个用于存储外星人的空列表aliens = []#创建三十个绿色的外星人for alien_number in range(30): new_alien = {'color' : 'green' , 'points' : 5 , 'speed' : 'slow'} aliens.append(new_alien)#显示前五个外星人for alien in aliens[:5]: print(alien)print(\"......\")#显示创建了多少外星人print(\"Total number of aliens: \" + str(len(aliens))) 输出结果如下: 1234567{'color': 'green', 'points': 5, 'speed': 'slow'}{'color': 'green', 'points': 5, 'speed': 'slow'}{'color': 'green', 'points': 5, 'speed': 'slow'}{'color': 'green', 'points': 5, 'speed': 'slow'}{'color': 'green', 'points': 5, 'speed': 'slow'}......Total number of aliens: 30 在上述例子中,虽然每个外星人都具有相同特征,但在Python看来,每个外星人都是独立的,我们可以独立地修改每个外星人: 12345678910111213aliens = []for alien_number in range(30): new_alien = {'color' : 'green' , 'points' : 5 , 'speed' : 'slow'} aliens.append(new_alien)for alien in aliens[0:3]: if alien['color'] == 'green': alien['color'] = 'yellow' alien['points'] = 10 alien['speed'] = 'medium'for alien in aliens[:5]: print(alien)print(\"......\")print(\"Total number of aliens: \" + str(len(aliens))) 输出结果如下: 1234567{'color': 'yellow', 'points': 10, 'speed': 'medium'}{'color': 'yellow', 'points': 10, 'speed': 'medium'}{'color': 'yellow', 'points': 10, 'speed': 'medium'}{'color': 'green', 'points': 5, 'speed': 'slow'}{'color': 'green', 'points': 5, 'speed': 'slow'}......Total number of aliens: 30 - 5.3.2 在字典中存储列表 有时候需要将列表储存在字典中,而不是将字典储存在列表中:例一: 12345678910#储存所点比萨的信息pizza = { 'crust' : 'thick' , 'toppings' : ['mushrooms' , 'extra chees'] , }#概述所点的比萨print(\"You ordered a \" + pizza['crust'] + \"-crust pizza\" + \"with the following toppings :\" )for topping in pizza['toppings']: print(\"\\t\" + topping) 输出结果如下: 123You ordered a thick-crust pizzawith the following toppings : mushrooms extra chees 例二: 12345678910favorite_languages = { 'jen' : ['python' , 'ruby'] , 'sarah' : ['c'] , 'edward' : ['go' , 'ruby'] , 'phil' : ['python' , 'java'] , }for name , languages in favorite_languages.items(): print(\"\\n\" + name.title() + \"'s favorite languages are:\") for language in languages: print(\"\\t\" + language.title()) 输出结果如下: 123456789101112131415Jen's favorite languages are: Python RubySarah's favorite languages are: CEdward's favorite languages are: Go RubyPhil's favorite languages are: Python Java - 5.3.3 在字典中存储字典 123456789101112131415161718users = { 'aeinstein' : { 'first' : 'albert' , 'last' : 'einstein' , 'location' : 'princeton' , } , 'mcurie' : { 'first' : 'marie' , 'last' : 'curie' , 'location' : 'paris' , } , }for username , user_info in users.items(): print(\"\\nUsername : \" + username) full_name = user_info['first'] + \" \" + user_info['last'] location = user_info['location'] print(\"\\tFull name : \" + full_name.title()) print(\"\\tlocation : \" + location .title()) 输出结果如下: 12345678Username : aeinstein Full name : Albert Einstein location : PrincetonUsername : mcurie Full name : Marie Curie location : Paris","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"基础学习","slug":"Python3-学习笔记/基础学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/基础学习/"}],"tags":[{"name":"字典","slug":"字典","permalink":"https://www.itrhx.com/tags/字典/"}]},{"title":"Python3 基础学习笔记 C04","slug":"A10-Python3-basic-C04","date":"2018-10-23T16:57:39.886Z","updated":"2019-09-24T12:45:32.554Z","comments":true,"path":"2018/10/24/A10-Python3-basic-C04/","link":"","permalink":"https://www.itrhx.com/2018/10/24/A10-Python3-basic-C04/","excerpt":"Python3 基础学习笔记第四章 —— 【if语句】","text":"Python3 基础学习笔记第四章 —— 【if语句】 - 4.1 一个简单的数列 给定一个汽车列表,将其中每一辆汽车的名称打印出来,要求打印 ‘bmw’ 时所有字母都要大写,其余名称只需要首字母大写: 123456cars = ['audi' , 'bmw' , 'subaru' , 'toyota']for car in cars: if car == 'bmw': print(car.upper())else: print(car.title()) 输出结果如下: 1234AudiBMWSubaruToyota - 4.1.1 检查特定值是否包含在列表当中 要判断特定的值是否已包含在列表当中,可使用关键字 in 1234user_names = ['andia' , 'david' , 'liwa']user = 'andia'if user in user_names: print(user.title() + \"is in user_name.\") 输出结果如下: 1Andiais in user_name. 要判断特定的值是否不包含在列表当中,可使用关键字 not in 1234user_names = ['andia' , 'david' , 'liwa']user = 'kivle'if user not in user_names: print(user.title() + \"is not in user_name.\") 输出结果如下: 1Kivleis not in user_name. - 4.2 if-else 语句 1234567age = input(\"请输入你的年龄查看是否可以去网吧:\")if int(age) >= 18: print(\"You are old enough to go to the net bar!\") print(\"You should go to net bar less,study more!\")else: print(\"You are too young to go to the net bar!\") print(\"Wait until you are 18 to go to the net bar!\") 分别输入19和15,输出结果如下: 123请输入你的年龄查看是否可以去网吧:19You are old enough to go to the net bar!You should go to net bar less,study more! 123请输入你的年龄查看是否可以去网吧:15You are too young to go to the net bar!Wait until you are 18 to go to the net bar! - 4.3 if-elif-else 结构 12345678age = 12if age < 4: price = 0elif age < 18: price = 5else: price = 10print(\"Your admission cost is $\" + str(price) + \".\") 输出结果如下: 1Your admission cost is $5. - 4.3.1 使用多个 elif 代码块 12345678910age = 20if age < 4: price = 0elif age < 18: price = 5elif age < 65: price = 15else: price = 10print(\"Your admission cost is $\" + str(price) + \".\") 输出结果如下: 1Your admission cost is $15. - 4.3.2 省略 else 代码块 Python并不要求 if-elif 结构后面必须有 else 代码块: 12345678910age = 20if age < 4: price = 0elif age < 18: price = 5elif age < 65: price = 15elif age >= 65: price = 10print(\"Your admission cost is $\" + str(price) + \".\") 输出结果仍与3.3.1一样 - 4.4 测试多个条件 if-elif-else结构功能强大,但仅适用于只有一个条件满足的情况:遇到通过了的测试后,Python就会跳过余下的测试: 12345678 names = ['Zhangshan' , 'Wanger']if 'Zhangshan' in names: print(\"Zhangshan is here!\")if 'Wanger' in names: print(\"Wanger is here!\")if 'Xiaoming' in names: print(\"Xiaoming is here!\")print(\"All the students are here!\") 输出结果如下: 123Zhangshan is here!Wanger is here!All the students are here! 相同的程序,如果使用 if-elif-else 结构,代码将不能正确运行: 12345678names = ['Zhangshan' , 'Wanger']if 'Zhangshan' in names: print(\"Zhangshan is here!\")elif 'Wanger' in names: print(\"Wanger is here!\")elif 'Xiaoming' in names: print(\"Xiaoming is here!\")print(\"All the students are here!\") 输出结果如下:12Zhangshan is here!All the students are here! 总之,如果我们只想执行一个代码块,就使用 if-elif-else 结构;如果要运行多个代码块,就必须使用一系列独立的 if 语句! - 4.5 使用 if 语句处理列表 - 4.5.1 检查特殊元素对3.4例子改版,加入姓名 ‘Xiaoming’,当检索到Xiaoming时告诉他,他妈妈叫他回家吃饭1234567names = ['Zhangshan' , 'Wanger' , 'Xiaoming']for name in names: if name == 'Xiaoming': print(\"Xiaoming,Your mother told you to go home for dinner!\") else: print(name +\"is here!\")print(\"All the students are here!\") 输出结果如下: 1234Zhangshanis here!Wangeris here!Xiaoming,Your mother told you to go home for dinner!All the students are here! - 4.5.2 确定列表不是空的 在检索姓名前检查姓名是否为空,不为空则打印出所有姓名,为空则提示没有姓名: 1234567names = []if names: for name in names: print(name +\" is here!\") print(\"All the students are here!\")else: print(\"There is no students!\") 输出结果如下: 1There is no students! 在if语句中将列表名用在条件表达式中时,Python将在列表至少包含一个元素时返回Ture,并在列表为空时返回False - 4.5.3 使用多个列表 两个列表names_1和names_2,要求输出既在names_2中又在names_1中的元素: 123456names_1 = ['Zhangshan' , 'Liyang' , 'Wanger' , 'Tangyang' , 'Xiaoming']names_2 = ['Liyang' , 'Zhangwei' , 'Tangyang']for names in names_2: if names in names_1: print(names +\" is here!\")print(\"All the students are here!\") 输出结果如下: 123Liyang is here!Tangyang is here!All the students are here!","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"基础学习","slug":"Python3-学习笔记/基础学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/基础学习/"}],"tags":[{"name":"if语句","slug":"if语句","permalink":"https://www.itrhx.com/tags/if语句/"}]},{"title":"Python3 基础学习笔记 C03","slug":"A09-Python3-basic-C03","date":"2018-10-11T15:03:18.487Z","updated":"2019-09-24T12:45:28.649Z","comments":true,"path":"2018/10/11/A09-Python3-basic-C03/","link":"","permalink":"https://www.itrhx.com/2018/10/11/A09-Python3-basic-C03/","excerpt":"Python3 基础学习笔记第三章 —— 【操作列表】","text":"Python3 基础学习笔记第三章 —— 【操作列表】 - 3.1遍历整个列表 使用 for 循环来遍历整个列表: 123names = ['alice' , 'david' , 'liwei']for name in names:print(name) 输出结果如下: 123alicedavidliwei for循环让Python从列表names中取出一个名字,并将其储存在变量name中,最后 让Python打印前面储存到变量name中的名字,对于列表中的每个名字,Python都将 重复执行后两行代码,将列表names中的每个名字都打印出来 - 3.1.1在for循环中执行更多的操作 在for循环中,可对每个元素执行任何操作,下面对前面的示例进行扩展: 例一:123names = ['alice' , 'david' , 'liwei']for name in names: print(name.title() + \", that was a good man!\") 输出结果如下: 123Alice, that was a good man!David, that was a good man!Liwei, that was a good man! 例二: 12345names = ['alice' , 'david' , 'liwei']for name in names: print(name.title() + \", that was a good man!\") print(\"I can't wait to see you again,\" + name.title() + \".\\n\")print(\"Nice to meet you!\") 输出结果如下: 12345678910Alice, that was a good man!I can't wait to see you again,Alice.David, that was a good man!I can't wait to see you again,David.Liwei, that was a good man!I can't wait to see you again,Liwei.Nice to meet you! - 3.2 range()函数 Python使用range()函数能够轻松地生成一系列的数字 Python3 range() 函数返回的是一个可迭代对象(类型是对象),而不是列表类型, 所以打印的时候不会打印列表; Python3 list() 函数是对象迭代器,可以把range()返回的可迭代对象转为一个列表,返回的变量类型为列表; Python2 range() 函数返回的是列表 例一:12for i in range(1,5): print(i) 输出结果如下: 12341234 例二:12for i in range(5): print(i) 输出结果如下:1234501234 例三:123456789101112>>> list(range(5))[0, 1, 2, 3, 4]>>> list(range(0))[]>>>list(range(0, 30, 5))[0, 5, 10, 15, 20, 25]>>> list(range(0, 10, 2))[0, 2, 4, 6, 8]>>> list(range(0, -10, -1))[0, -1, -2, -3, -4, -5, -6, -7, -8, -9]>>> list(range(1, 0))[] 例四: 12345squares = []for value in range(1,11): square = value ** 2 squares.append(square)print(squares) 输出结果如下: 1[1, 4, 9, 16, 25, 36, 49, 64, 81, 100] - 3.2.1 对数字列表执行简单的统计计算 1234567>>> digits = [1, 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 0]>>> min(digits)0>>>max(digits)9>>>sum(digits)45 - 3.2.2 列表解析 列表解析能够让比如3.2中的例四更加简化,只需要一行代码就能生成这样的列表,列表解析将for循环和创建新元素的代码合并成一行,并自动附加新元素: 12squares = [value ** 2 for value in range(1,11)]print(squares) 在这个示例中,for循环为for value in range(1,11),它将值1~10提供给表达式value ** 2输出结果如下: 1[1, 4, 9, 16, 25, 36, 49, 64, 81, 100] - 3.3 使用列表的一部分 处理列表的部分元素——Python称之为切片 - 3.3.1 切片 1234567891011list = ['a','b','c','d','e','f']print(list[:]) #省略全部,代表截取全部内容,可以用来将一个列表拷给另一个列表print(list[:3]) #省略起始位置的索引,默认起始位置从头开始,结束位置索引为2print(list[3:]) #省略结束位置的索引,默认结束位置为最后一个,开始位置索引为3print(list[1:4]) #开始位置索引为1,结束位置索引为3,顾头不顾尾print(list[4:1]) #从左到右索引,因此为空值print(list[-1:-3]) #从左到右索引,因此为空值print(list[-3:-1]) #开始位置索引为倒数第三个,结束位置索引为倒数第二个print(list[1:5:2]) #开始位置索引为1,结束位置索引为4,间隔2print(list[5:1:-1]) #反向取值,开始位置索引为5,结束位置索引为2print(list[::-1]) #反向取值,反向输出列表 - 3.3.2 遍历列表 1234players = ['charles' , 'martina' , 'michael' , 'florence' , 'eli']print(\"Here are the first three players on my team:\")for player in players[:3]: print(player.title()) 输出结果如下: 1234Here are the first three players on my team:CharlesMartinaMichael - 3.3.3 复制列表 要复制列表,可以创建一个包含整个列表的切片,方法是同时省略起始索引和终止索引([:]),这让Python创建一个始于第一个元素,终止于最后一个元素的切片,即复制整个列表: 123456my_foods = ['pizza' , 'falafel' , 'carrot cake']friend_foods = my_foods[:]print(\"My favorite foods are:\")print(my_foods)print(\"\\nMy friend's favorite foods are:\")print(friend_foods) 输出结果如下: 12345My favorite foods are:['pizza', 'falafel', 'carrot cake']My friend's favorite foods are:['pizza', 'falafel', 'carrot cake'] 为核实我们的确有两个列表,下面在每个列表中都添加一种食品,并核实每个列表都记录了相应人员喜欢的食品:12345678910my_foods = ['pizza' , 'falafel' , 'carrot cake']friend_foods = my_foods[:]my_foods.append('cannoli')friend_foods.append('ice cream')print(\"My favorite foods are:\")print(my_foods)print(\"\\nMy friend's favorite foods are:\")print(friend_foods) 输出结果如下: 12345My favorite foods are:['pizza', 'falafel', 'carrot cake', 'cannoli']My friend's favorite foods are:['pizza', 'falafel', 'carrot cake', 'ice cream'] 输出结果表明,’cannoli’包含在我喜欢的食品列表中,而’ice cream’没有;’ice cream’包含在我朋友喜欢的食品中,而’cannoli’没有,假如我们只是简单的将my_foods赋给friend_foods,就不能得到两个列表。下面是错误示例: 12345678910my_foods = ['pizza' , 'falafel' , 'carrot cake']friend_foods = my_foods #错误写法my_foods.append('cannoli')friend_foods.append('ice cream')print(\"My favorite foods are:\")print(my_foods)print(\"\\nMy friend's favorite foods are:\")print(friend_foods) 错误示例输出结果如下: 12345My favorite foods are:['pizza', 'falafel', 'carrot cake', 'cannoli', 'ice cream']My friend's favorite foods are:['pizza', 'falafel', 'carrot cake', 'cannoli', 'ice cream'] - 3.4 元组 Python将不能修改的值称为不可变的,而不可变的列表被称为元组 - 3.4.1 定义元组 元组看起来就像是列表,但元组使用圆括号而不是方括号来标识,定义元组后,就可以使用索引来访问其元素,就像访问列表元素一样: 123dimensions = (200,50)print(dimensions[0])print(dimensions[1]) 输出结果如下: 1220050 如果尝试修改元组中元素的值,将会导致Python返回类型错误消息,由于试图修改元组的操作是被禁止的,因此Python指出不能给元组的元素赋值: 12dimensions = (200,50)dimensions[0] = 300 将会报错: 1234Traceback (most recent call last): File \"dimensions.py\", line 2, in <module> dimensions[0] = 300TypeError: 'tuple' object does not support item assignment - 3.4.2 遍历元组中所有的值 像列表一样,元组也可以使用for循环来遍历元组中的所有值: 例一:123dimensions = (200,100,50,6)for dimension in dimensions: print(dimension) 输出结果如下: 1234200100506 例二: 123dimensions = (200,100,50,6)for dimension in dimensions[:3]: print(dimension) 输出结果如下: 12320010050 - 3.4.3 修改元组变量 虽然不能修改元组元素,但是可以给储存元组的变量赋值: 123456789dimensions = (200,50)print(\"Original dimensions:\")for dimension in dimensions: print(dimension) dimensions = (400,100)print(\"\\nModified dimensions:\")for dimension in dimensions: print(dimension) 输出结果如下: 1234567Original dimensions:20050Modified dimensions:400100 我们首先定义了一个元组,并将其储存的尺寸打印了出来;然后将一个新元组储存到变量dimensions中,打印新的尺寸;相比于列表,元组是更简单的数据结构。如果需要储存的一组值在程序的整个生命周期内都不变,可使用元组","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"基础学习","slug":"Python3-学习笔记/基础学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/基础学习/"}],"tags":[{"name":"操作列表","slug":"操作列表","permalink":"https://www.itrhx.com/tags/操作列表/"}]},{"title":"Python3 基础学习笔记 C02","slug":"A08-Python3-basic-C02","date":"2018-09-15T17:59:48.972Z","updated":"2019-09-24T12:45:24.985Z","comments":true,"path":"2018/09/16/A08-Python3-basic-C02/","link":"","permalink":"https://www.itrhx.com/2018/09/16/A08-Python3-basic-C02/","excerpt":"Python3 基础学习笔记第二章 —— 【列表】","text":"Python3 基础学习笔记第二章 —— 【列表】 - 2.1列表是什么 列表由一系列按特定顺序的元素组成,在 Python 中用方括号( [ ] )来表示列表,并用逗号来分隔其中的元素,例: 12345list1 = ['a','b','c','d','e','f']list2 = ['abc', 'xyz', 2018, 2020]list3 = [1, 2, 3, 4, 5 ,6]list4 = [\"a\", \"b\", \"c\", \"d\"]print(list1, list2, list3 ,list4) 输出结果如下: 1['a', 'b', 'c', 'd', 'e', 'f'] ['abc', 'xyz', 2018, 2020] [1, 2, 3, 4, 5, 6] ['a', 'b', 'c', 'd'] - 2.1.1访问列表元素 列表是有序集合,因此要访问列表的元素,只需要将该元素的位置或索引告诉Python即可,注意:在Python中的第一个列表元素的索引为0,而不是1 12345list = ['a','b','c','d','e','f']print(list[0])print(list[3])print(list[-1]) #Python为访问最后一个列表元素提供了一种特殊语法,通过将索引指定为-1,可以让Python返回最后一个列表元素print(list[-3]) 输出结果如下: 1234adfd - 2.1.2列表切片 1234567891011list = ['a','b','c','d','e','f']print(list[:]) #省略全部,代表截取全部内容,可以用来将一个列表拷给另一个列表print(list[:3]) #省略起始位置的索引,默认起始位置从头开始,结束位置索引为2print(list[3:]) #省略结束位置的索引,默认结束位置为最后一个,开始位置索引为3print(list[1:4]) #开始位置索引为1,结束位置索引为3,顾头不顾尾print(list[4:1]) #从左到右索引,因此为空值print(list[-1:-3]) #从左到右索引,因此为空值print(list[-3:-1]) #开始位置索引为倒数第三个,结束位置索引为倒数第二个print(list[1:5:2]) #开始位置索引为1,结束位置索引为4,间隔2print(list[5:1:-1]) #反向取值,开始位置索引为5,结束位置索引为2print(list[::-1]) #反向取值,反向输出列表 输出结果如下: 12345678910['a', 'b', 'c', 'd', 'e', 'f']['a', 'b', 'c']['d', 'e', 'f']['b', 'c', 'd'][][]['d', 'e']['b', 'd']['f', 'e', 'd', 'c']['f', 'e', 'd', 'c', 'b', 'a'] - 2.1.3使用列表中的各个值 可像使用其他变量一样使用列表中的各个值,例如,我们可以使用拼接根据列表中的值来创建消息: 123list = ['python', 'c', 'c++', 'java', 'php']message = \"My favorite language is \" + list[0].title() + \"!\"print(message) 输出结果如下: 1My favorite language is Python! - 2.1.4修改元素 修改列表元素的语法与访问列表元素的语法类似,要修改列表元素,可指定列表名和要修改的元素的索引,再次指定该元素的新值: 1234names = ['zhangsan', 'lishi', 'wanger', 'liming', 'xiaowang']print(names)names[1] = 'lifang'print(names) 输出结果如下: 12['zhangsan', 'lishi', 'wanger', 'liming', 'xiaowang']['zhangsan', 'lifang', 'wanger', 'liming', 'xiaowang'] - 2.1.5添加元素 - 使用方法 append() 在列表末尾添加元素 1234list = ['a', 'b', 'c', 'd', 'e', 'f']print(list)list.append('g')print(list)输出结果如下:12['a', 'b', 'c', 'd', 'e', 'f']['a', 'b', 'c', 'd', 'e', 'f', 'g'] - 使用方法 insert() 在列表指定位置添加元素 1234list = ['a', 'b', 'c', 'd', 'e', 'f']print(list)list.insert(2,\"h\") #其中括号里的数字表示要插入的位置,此后面的元素将右移一个位置print(list) 输出结果如下: 12['a', 'b', 'c', 'd', 'e', 'f']['a', 'b', 'h', 'c', 'd', 'e', 'f', 'g'] - 2.1.6删除元素 - 使用 del 语句删除元素 1234list = ['a', 'b', 'c', 'd', 'e', 'f']print(list)del list[3]print(list) 输出结果如下: 12list = ['a', 'b', 'c', 'd', 'e', 'f']list = ['a', 'b', 'c', 'e', 'f'] - 使用方法 pop() 删除最后一个元素方法 pop() 可以删除列表末尾的元素,并让你能够接着使用它。术语弹出(pop)源自这样的类比:列表就像是一个栈,而删除列表末尾的元素就相当于弹出栈顶元素:12345list = ['a', 'b', 'c', 'd', 'e', 'f']print(list)new_list = list.pop()print(list)print(new_list)输出结果如下:123['a', 'b', 'c', 'd', 'e', 'f']['a', 'b', 'c', 'd', 'e']f - 使用方法 pop() 删除任意位置元素可以使用 pop() 来删除列表中任何位置的元素,只需要在括号中指定要删除的元素的索引即可:12345list = ['a', 'b', 'c', 'd', 'e', 'f']print(list)new_list = list.pop(1)print(list)print(new_list)输出结果如下:123['a', 'b', 'c', 'd', 'e', 'f']['a', 'c', 'd', 'e', 'f']b - 使用方法 remove() 删除未知位置元素当我们不知道元素的位置,只知道元素的值的时候,就可以使用方法 remove()1234list = ['a', 'b', 'c', 'd', 'e', 'f']print(list)list.remove('d')print(list)输出结果如下:12['a', 'b', 'c', 'd', 'e', 'f']['a', 'b', 'c', 'e', 'f'] # - 2.1.7使用方法 index() 查找指定元素位置 12list = [\"a\", \"b\", \"c\", \"d\", \"e\", \"a\"]print(list.index('c')) 输出结果如下: 12 - 2.1.8使用方法 count() 统计指定元素数量 12list = [\"a\", \"b\", \"c\", \"d\", \"e\", \"a\"]print(list.count('a')) 输出结果如下: 12 - 2.1.9清空列表 123list = [\"a\", \"b\", \"c\", \"d\", \"e\", \"a\"]list.clear()print(list) 输出结果如下: 1[] - 2.2组织列表 在创建的列表中,元素的排列顺序常常是无法预测的,因为我们并非总能控制用户提供数据的顺序。有时候,我们希望保留列表元素最初的排列顺序,而有时候又需要调整排列顺序。Python提供了很多组织列表的方式,可根据具体情况选用 - 2.2.1使用方法 sort() 对列表进行永久排序 使用方法 sort() 可以对列表按照特殊符号,数字,大写字母,小写字母顺序进行永久排序: 123cars = ['bmw', 'audi', 'toyota', 'subaru']cars.sort()print(cars) 输出结果如下: 1['audi', 'bmw', 'subaru', 'toyota'] 还可以按与字母顺序相反的顺序排列列表元素,只需要向 sort() 方法传递参数 reverse = True 就可以了: 123cars = ['bmw', 'audi', 'toyota', 'subaru']cars.sort(reverse = True)print(cars) 输出结果如下: 1['toyota', 'subaru', 'bmw', 'audi'] - 2.2.2使用函数 sorted() 对列表进行临时排序 要保留列表元素原来的排列顺序,同时以特定的顺序呈现它们,可使用函数sorted()。函数sorted()让你能够按特定顺序显示列表元素,同时不影响它们在列表中的原始排列顺序: 123456789cars = ['bmw', 'audi', 'toyota', 'subaru']print(\"Here is the original list:\")print(cars)print(\"\\nHere is the sorted list:\")print(sorted(cars))print(\"\\nHere is the sorted reverse list:\")print(sorted(cars, reverse=True))print(\"\\nHere is the original list again:\")print(cars) 输出结果如下: 1234567891011Here is the original list:['bmw', 'audi', 'toyota', 'subaru']Here is the sorted list:['audi', 'bmw', 'subaru', 'toyota']Here is the sorted reverse list:['toyota', 'subaru', 'bmw', 'audi']Here is the original list again:['bmw', 'audi', 'toyota', 'subaru'] - 2.2.3使用方法 reverse() 对列表进行反向排序 要反转列表元素的排列顺序,可使用方法 reverse() 123cars = ['bmw', 'audi', 'toyota', 'subaru']cars.reverse()print(cars) 输出结果如下: 1['subaru', 'toyota', 'audi', 'bmw'] - 2.2.4确定列表的长度 使用函数 len() 可以快速获悉列表的长度: 123>>>cars = ['bmw', 'audi', 'toyota', 'subaru']>>>len(cars)4 - 2.2.5合并列表 - 使用方法 extend() 合并列表 12345list1 = [1, 2, 3, 4]list2 = ['a', 'b', 'c', 'd']list1.extend(list2) #将列表list2添加到list1当中去print(list1)print(list2) 输出结果如下: 12[1, 2, 3, 4, 'a', 'b', 'c', 'd']['a', 'b', 'c', 'd'] - 使用 “+” 号合并列表 1234list1 = [1, 2, 3, 4]list2 = ['a', 'b', 'c', 'd']print(list1 + list2)print(list2 + list1) 输出结果如下: 12[1, 2, 3, 4, 'a', 'b', 'c', 'd']['a', 'b', 'c', 'd', 1, 2, 3, 4] - 使用切片合并列表 1234567891011121314list1 = [1, 2, 3, 4]list2 = ['a', 'b', 'c', 'd']list1[len(list1) : len(list1)] = list2 #len(list1)代表要将list2插入list1中的位置print(list1)list1 = [1, 2, 3, 4]list2 = ['a', 'b', 'c', 'd']list1[0 :0] = list2print(list1)list1 = [1, 2, 3, 4]list2 = ['a', 'b', 'c', 'd']list1[1:1] = list2print(list1) 输出结果如下: 123[1, 2, 3, 4, 'a', 'b', 'c', 'd']['a', 'b', 'c', 'd', 1, 2, 3, 4][1, 'a', 'b', 'c', 'd', 2, 3, 4]","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"基础学习","slug":"Python3-学习笔记/基础学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/基础学习/"}],"tags":[{"name":"列表","slug":"列表","permalink":"https://www.itrhx.com/tags/列表/"}]},{"title":"Python3 基础学习笔记 C01","slug":"A07-Python3-basic-C01","date":"2018-09-13T12:47:09.608Z","updated":"2019-12-29T07:27:42.544Z","comments":true,"path":"2018/09/13/A07-Python3-basic-C01/","link":"","permalink":"https://www.itrhx.com/2018/09/13/A07-Python3-basic-C01/","excerpt":"Python3 基础学习笔记第一章 —— 【变量和简单数据类型】","text":"Python3 基础学习笔记第一章 —— 【变量和简单数据类型】 - 1.1变量的命名和使用 变量名只能包含字母、数字和下划线。变量名可以字母或者下划线打头,但不能以数字开头,例如,可以将变量命名为message_1,但不能将其命名为1_message 变量名不能包含空格,但可使用下划线来分割其中的单词,例如,变量名greeting_message可行,但变量名greeting message会引发错误 不要将Python关键字和函数名用作变量名,即不要使用Python保留用于特殊用途的单词,如print 变量名应既简短又具有描述性,例如,name比n好,student_name比s_n好,name_length比length_of_persons_name好 慎用小写字母l和大写字母O,因为它们可能被人看错成数字1和0 - 1.2字符串 字符串就是一系列字符,在Python中,用引号括起来的都是字符串,其中的引号可以是单引号也可以双引号: 12\"This is a string.\"'This is also a string.' 这种灵活性让我们能够在字符串中包含引号和撇号: 123'I told my friend,\"Python is my favorite language!\"'\"The language 'Python' is named er Monty Python,not the snake.\"\"One of Python's strengths is i diverse and supportive community.\" - 1.2.1使用方法修改字符串的大小写三种处理方法如下:123title() #将字符串每个单词的首字母都改为大写upper() #将字符串的每个字母都改为大写lower() #将字符串的每个字母都改为小写 例如:1234message = \"I love you!\"print(name.title())print(name.upper())print(name.lower()) 输出结果如下:123I Love You!I LOVE YOU!i love you! - 1.2.2合并(拼接)字符串Python使用加号(+)来合并字符串,举例说明: 12345first_name = \"I\"second_name = \"love\"third_name = \"python\"full_name = first_name + \" \" + second_name + \" \" + third_timeprint(full_name.title() + \"!\") 输出结果如下: 1I Love Python! - 1.2.3使用制表符或换行符来添加空白添加横向制表符: 12>>>print(\"\\tPython\") Python 添加换行符: 12345>>>print(\"C\\nC++\\nPython\\nJavaScript\")CC++PythonJavaScript 附表:Python转义符 - 1.2.4删除空白在Python中可用 lstrip()、rstrip()、strip() 分别删除字符串开头、结尾、全部的空白,举例说明: 123456789>>>message = ' python '>>>message' python '>>>message.lstrip()'python '>>>message.rstrip()' python'>>>message.strip()'python' 如果要永久删除字符串中的空白,必须将删除操作的结果存回到变量中: 1234>>>message = ' python '>>>message = message.strip()>>>message'python' - 1.3数字在编程中,经常使用数字来记录游戏得分、表示可视化数据、储存Web应用信息等。Python根据数字的用法以不同的方式处理它们 - 1.3.1整数在Python中,可对整数执行加(+)减(-)乘(*)除(/)乘方(**)运算,同时也支持运算次序: 12345678910111213141516>>>3 + 25>>>3 - 21>>>3 * 26>>>3 \\ 21.5>>>3 ** 29>>>3 ** 327>>>2 + 3 * 414>>>(2 + 3) * 420 - 1.3.2浮点数Python将带小数点的数字都称为浮点数: 1234>>>0.1 + 0.10.2>>>2 * 0.20.4 需要注意的是,结果包含的小数位可能是不确定的,就现在而言,暂时忽略多余的小数位即可: 1234>>>0.2 + 0.10.30000000000000004>>>3 * 0.10.30000000000000004 - 1.3.3使用函数 str() 避免错误错误例子: 123age = 23message = \"Happy \" + age + \"rd Birthday!\"print(message) 运行时会报错: 1234Traceback (most recent call last): File \"birthday.py\", line 2, in <module> message = \"Happy \" + age + \"rd Birthday!\"TypeError: must be str, not int 这是一个类型错误,意味着Python无法识别我们使用的信息。在这个例子中,Python发现我们使用了一个值为整数(int)的变量,但它不知道该如何解读这个值,这个变量表示的可能是数值23,也可能是字符2和3。像上面这样的字符串中使用整数时,需要显式地指出我们希望Python将这个整数用作字符串。为此,可调用函数 str(),它让Python将非字符串值表示为字符串: 123age = 23message = \"Happy \" + str(age) + \"rd Birthday!\"print(message) 输出结果如下: 1Happy 23rd Birthday! - 1.4注释注释让我们能够使用自然语言在程序中添加说明,Python中注释有三种方法: 123456789print(\"Hello Python!\")#这是单行注释'''这是多行注释这是多行注释'''\"\"\"这也是多行注释这也是多行注释\"\"\"","categories":[{"name":"Python3 学习笔记","slug":"Python3-学习笔记","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/"},{"name":"基础学习","slug":"Python3-学习笔记/基础学习","permalink":"https://www.itrhx.com/categories/Python3-学习笔记/基础学习/"}],"tags":[{"name":"数据类型","slug":"数据类型","permalink":"https://www.itrhx.com/tags/数据类型/"},{"name":"变量","slug":"变量","permalink":"https://www.itrhx.com/tags/变量/"}]},{"title":"VMware Pro 14 安装 Ubuntu 18.04 详细教程","slug":"A06-install-ubuntu18.04","date":"2018-09-09T13:14:29.532Z","updated":"2020-03-14T05:48:49.164Z","comments":true,"path":"2018/09/09/A06-install-ubuntu18.04/","link":"","permalink":"https://www.itrhx.com/2018/09/09/A06-install-ubuntu18.04/","excerpt":"","text":"1.下载安装 VMware Workstation Pro 14 进入 VMware 官网或者在软件商店下载最新版VMware虚拟机并安装 2.下载 Ubuntu 18.04 系统 进入 Ubuntu 官网,下载最新版 Ubuntu 系统镜像 3.在 VMware 中创建虚拟机打开安装好的 VMware Workstation Pro 14,选择创建新的虚拟机 在新建虚拟机向导中选择自定义(高级) 默认直接下一步,直到出现下图,再选择稍后安装操作系统 选择客户机操作系统为 Linux ,如果你电脑是32位就选择 Ubuntu 版本,64位就选择 Ubuntu 64 位版本 更改虚拟机名称及存放位置 为虚拟机指定处理器数量,默认即可 为虚拟机分配内存,太大了可能会导致卡顿,太小了也不好,推荐内存大小即可 以下均选择默认即可 选择创建新虚拟磁盘 选择将虚拟磁盘储存为单个文件 默认下一步 点击完成 此时我们就可以在虚拟机左侧“我的计算机”下面看到刚刚创建的虚拟机 Ubuntu 64 位,单击 Ubuntu 64 位,选择“编辑虚拟机设置”, 再选择“CD/DVD(SATA)”,选择“使用ISO映像文件”,点击“浏览”,找到先前我们下载好的 Ubuntu 64 位镜像文件,点击“确定” 4.在虚拟机上安装 Ubuntu 系统单击 Ubuntu 64 位,选择“开启此虚拟机” 来到欢迎界面,选择好语言,点击“安装 Ubuntu” 选择键盘布局为“汉语” 更新和其他软件默认选择即可 安装类型选择“清除整个磁盘并安装 Ubuntu”,PS: 因为我们是新安装的系统,且在虚拟机中,所以可以选择清除整个磁盘,这个操作不会清除你原来电脑里面的东西 地区随便,在中国就行,默认即可 之后设置计算机名,密码 点击继续稍等一会就安装完成啦 安装过程中可能会出现的一些问题 1.在虚拟机上安装 Ubuntu 系统的过程中卡死不动 解决方法:关闭网络,重新安装即可 2.Ubuntu 不能全屏显示解决方法:方法①:安装 open-vm-tools: 1sudo apt-get install open-vm-tools 然后执行: 1sudo apt-get install open-vm* 重启即可全屏显示 方法②:在终端输入xrandr,并回车,我们就可以看到很多可以修改的分辨率,选择好分辨率后,比如我们要修改分辨率为 1920x1440 ,则在终端输入 xrandr -s 1920x1440,回车即可,注意 1920x1440 中间是小写字母 x,本人亲测此方法并不是很完美,不能完全适应屏幕 方法③:安装 VMware Tools:1、进入 Ubuntu 系统后,点击虚拟机上的【虚拟机】—>【安装 VMware Tools】,回到桌面即可看到一个 VMware Tools 的 图标2、复制 VMwareTools-10.0.10-4301679.tar.gz(版本根据自己的实际情况而定)到 home 目录下, 用命令 tar -xzvf VMwareTools-10.0.10-4301679.tar.gz 进行解压3、解压后 cd vmware_tools_distrib,打开终端4、输入“sudo ./vmware-install.pl”,输入用户密码后开始安装5、接下来会有很多地方需要你按 Enter或者 Yes6、当你看到出现 —the vmware team 的字样后就可以关闭窗口了,此时窗口就会自动全屏了,如果没有全屏,重启过后就可以了7、若还没有全屏显示,则将虚拟机的【查看】—>【自动调整大小】—>【自适应客户机】,都选上,即可实现全屏","categories":[{"name":"Linux","slug":"Linux","permalink":"https://www.itrhx.com/categories/Linux/"}],"tags":[{"name":"VMware","slug":"VMware","permalink":"https://www.itrhx.com/tags/VMware/"},{"name":"Ubuntu","slug":"Ubuntu","permalink":"https://www.itrhx.com/tags/Ubuntu/"}]},{"title":"主流 Markdown 编辑器推荐","slug":"A05-markdown-editor","date":"2018-08-29T15:02:46.857Z","updated":"2020-03-14T05:44:24.275Z","comments":true,"path":"2018/08/29/A05-markdown-editor/","link":"","permalink":"https://www.itrhx.com/2018/08/29/A05-markdown-editor/","excerpt":"","text":"Markdown ,2004年由 John Gruberis 设计和开发,是一种可以使用普通文本编辑器编写的标记语言,通过简单的标记语法,它可以使普通文本内容具有一定的格式,以下将介绍目前比较流行的一些 Markdown 编辑器(排名不分先后) - MarkdownPad 目前分为 MarkdownPad2 和 MarkdownPad Pro 版本,后者收费,我们使用前者足矣,用户可以通过键盘快捷键和工具栏按钮来使用或者移除 Markdown 各种语法格式,支持自定义配色方案、字体、大小和布局 、即时HTML预览、HTML和PDF导出,被很多人称赞为 Windows 平台最好用的 Markdown 编辑器,实用性强,仅支持 Windows 系统,个人觉得在 Windows 10 系统上界面并不是很好看,有时候添加音乐什么的,资源多了,实时预览会显示资源加载失败,点击此处访问 MarkdownPad 官网 - BookPad 无意间在 Microsoft Store 上发现的,完美搭配 Win10 系统,界面非常简洁漂亮,2017年9月份发布,大小30.82 MB,官方网站:https://sosfos.wordpress.com/ ,收费13人民币,可免费使用7天,各种功能应有尽有,和其他编辑器不相上下,本来想着百度百度看看有没有破解版,结果全网看不见 BookPad 的影子,估计是新出来的还不为人所知吧,可以直接在 Microsoft Store 搜索下载,或者点击链接获取:https://www.microsoft.com/store/apps/9N6P5ZH2SJSX - 小书匠 分为免费版和收费版,收费版¥20/年,其实免费版的功能已经足够强大了,多种编辑模式、多种主题选择、多种编辑器实现、丰富的语法支持、第三方同步、强大的文件管理功能,让人使用一次就爱上了它,支持 Windows 和 Web,推荐使用,点击此处访问小书匠官网 - TyporaTypora 同样支持 Windows、OS X 和 Linux,Typora 支持即时渲染技术,这也是与其他 Markdown 编辑器最显著的区别,支持数学编辑,可与 Word 直接格式转换,在 Pandoc 的支持下进行多种文档格式转换,Typora 适合那些对码字手速和排版顺畅度有要求的人群,譬如码农、网站小编等,点击此处访问 Typora 官网 - Visual Studio CodeVisual Studio Code 是众所周知的神器,是微软推出一款轻量级的文本编辑工具,类似于 Sublime,它已经默认集成 Markdown 文档编辑插件,原生就支持高亮 Markdown 的语法,但想要实时预览还需要选择 Markdown: Open Preview to the Side 命令实现,相关教程请点击此处,点击此处 访问 Visual Studio Code 官网 - MarxicoMarxico 中文名马克飞象,提供桌面客户端以及离线 Chrome App,支持移动端 Web,可以直接把文本存到印象笔记,点击此处访问 Marxico,点击此处访问 马克飞象 - Sublime Text 3Sublime Text 3 是基于 Vim 开发的跨平台代码编辑器,收费80美元,好像可以免费试用,支持 OS X、Windows、Ubuntu 等 UNIX 及 Linux 操作系统,由于其功能的多样性而广受好评,界面简约大方,定位专业,原生支持的编程语言就多达十几种,通过第三方插件,还能实现更多语法的支持,其中就包括 Markdown ,但也有个缺点,就是不能实时预览,但是用户可以通过 Markdown Preview 的插件实现对 Markdown 的预览,具体教程请点击此处查看,点击此处访问 Sublime Text 官网 - Mou Mou 是一款由国人独立开发者罗晨开发的实时预览型 Markdown 编辑器,仅支持 OS X操作系统,是目前同类应用中对汉字兼容性最好的作品,也是目前最好用的免费 Markdown 编辑器,提供语法高亮、在线预览、同步滚动、全屏模式,支持自定保存、自动匹配,允许自定义主题,支持 CSS,HTML 和 PDF 导出等功能,点击此处访问 Mou 官网 - AtomAtom 是 Github 专门为程序员推出的一个跨平台文本编辑器,具有简洁和直观的图形用户界面,并有很多有趣的特点:支持CSS,HTML,JavaScript等网页编程语言,当然也支持 Markdown ,支持宏,自动完成分屏功能,集成了文件管理器,点击此处访问 Atom 官网 - Smark国人编写的开源软件,Windows / Linux 等主流系统跨平台支持,完美支持 LaTex 数学公式、脚注、尾注等,支持使用本地 MathJax 调用,不需要在线访问 MathJax CDN,用户可配置的 Markdown 语法高亮显示,美观整洁,多种格式文件导出支持,简洁友好的界面布局,完备的各类快捷键,能极大地提高工作效率,点击此处访问 Smark 官网 - HaroopadHaroopad 覆盖三大主流桌面系统,支持 Windows、OS X 和 Linux,多种主题样式供你选择,语法标亮支持 54 种编程语言,该工具重点推荐 Ubuntu/Linux 用户使用,点击此处访问 Haroopad 官网 - CuteMarkEdCuteMarkEd 是一个基于qt5的跨平台的 Markdown 编辑器,开源的, 提供实时 HTML 预览、数学表达式、源码高亮和PDF导出,点击此处 访问 CuteMarkEd 官网 - MarkPadMarkPad 是款开源的 Markdown 编辑器,与 Window 8 风格和谐友好的界面,可以直接在你的博客或者 GitHub 中打开、保存文档,直接将图片粘贴到 Markdown 文档中,点击此处访问 MarkPad 官网 - Cmd Markdown作业部落出品,是一款不错的工具和博客平台兼顾的产品,同时支持 Linux、Mac 和 Windows 操作系统,此外还提供 Web 在线创作,社交化批注、智能云同步,最简单的方法,满足多种写作需要,点击此处访问 Cmd Markdown 官网 - FarBox同样是一款不错的 Markdown 编辑器和博客平台兼顾的产品,让用户通过Dropbox(现在默认是自己的同步服务器)直接建立个人网站。FarBox编辑器免费,同时支持 Linux、Mac 和 Windows 操作系统,Farbox服务可以免费试用,在本地编辑器内写作自动同步发布在个人博客,对于希望有个人博客但却不愿折腾的小白来说,是个不错的选择,点击此处访问 FarBox 官网 - MiuMiu 是一款 Windows 下的 Markdown 编辑器,支持 Markdown 高亮、代码高亮、即时预览,以及可以快速发布到 Github Gist,小众软件,界面美观,已经找不到官网了,小众软件网有提供百度云下载,Miu 下载地址 - MacDownMacDown 引用了许多 Mou 的设计方式,仅支持 Mac ,开源免费,点击此处访问 MacDown 官网 - Ulysses一款由国外开发商 The Soulmen 制作的 Markdown 编辑器。与其它同类应用相比,Ulysses 最大的不同在于,它能根据内置的文件管理器,以及与 iCloud 云服务器的实时同步方案,达到最快捷的文章整理效率,支持OS X , iPad,26人民币每月,14天免费试用,点击此处访问 Ulysses 官网 - Byword一款轻量级的 Markdown 编辑器,支持Mac,iPhone和iPad,界面极简,功能强大,貌似要付费使用,点击此处 访问 Byword 官网 - MaHua一个在线编辑 Markdown 文档的编辑器,小众软件,VIM 快捷键支持,完美兼容 Github 的 Markdown 语法,界面稍许简陋,点击此处访问 MaHua - Dillinger来自国外的 Markdown 编辑器,漂亮强大,支持md、 html、pdf 文件导出,支持Dropbox、Github、Google Drive、Onedrive 一键保存,点击此处访问 Dillinger - CSDN中国专业IT社区CSDN (Chinese Software Developer Network) 创立于1999年,致力于为中国软件开发者提供知识传播、在线学习、职业发展等全生命周期服务。CSDN的在线编辑器功能强大,支持导出为HTML和md文件,注册账号后即可开始创作,点击此处访问CSDN官网 - 简书简书是一个优质的创作社区,你可以在线创作并发表到社区,是国内优质原创内容输出平台,简书从一开始就已经支持 Markdown 和富文本编辑,是一个为专门为作者打造的平台,点击此处访问简书官网 要细数 Markdown 编辑器的话,可能永远也数不尽,而且每个人的看法也不同,正所谓萝卜白菜各有所爱,什么编辑器不是最重要的,重要的是我们能写出优质的文章,不断学习进步!不断提升自我! 参考资料:《好用的Markdown编辑器一览》(By:月光)《10款流行的Markdown编辑器,总有一款适合你》(By:xiaoxiao_engineer)《解决作者们的焦虑:7 款优秀 Markdown 编辑工具推荐》(By:JailJT)","categories":[{"name":"Markdown","slug":"Markdown","permalink":"https://www.itrhx.com/categories/Markdown/"}],"tags":[{"name":"Markdown","slug":"Markdown","permalink":"https://www.itrhx.com/tags/Markdown/"},{"name":"编辑器","slug":"编辑器","permalink":"https://www.itrhx.com/tags/编辑器/"}]},{"title":"Hexo 博客主题个性化","slug":"A04-Hexo-blog-topic-personalization","date":"2018-08-27T13:25:24.452Z","updated":"2020-07-13T13:29:40.312Z","comments":true,"path":"2018/08/27/A04-Hexo-blog-topic-personalization/","link":"","permalink":"https://www.itrhx.com/2018/08/27/A04-Hexo-blog-topic-personalization/","excerpt":"","text":"加入 Hexo 博客交流群:924812033,有问题互相交流学习! 原 Material X 主题现已改名为 Volantis,部分教程可能已失效,失效请留言告知,谢谢! 本文将讲述一些博客主题的美化、实用功能的添加,本文以作者 luuman 的 spfk 主题和作者 xaoxuu 的 Material X 主题为例,文章会不定时进行更新。文章涉及有关参考资料、教程、链接如有侵权请联系我删除! 本文在CSDN的链接:《Hexo 博客优化之博客美化》、《Hexo 博客优化之实用功能添加》,Hexo 博客专栏,从前期搭建到后期美化,帮您解决常见问题:《Github/Coding Pages + Hexo》,对您有帮助就点个赞吧❤️ 请注意:不同主题可能方法有些不同,相同主题不同版本,配置方法也有所差异! 博客美化前提条件:有一定的前端基础,了解 HTML、CSS、JS,了解 CSS 预处理语言 Sass、Less、Stylus,搞懂 hexo 的目录结构。 博客美化通用步骤:选定主题,认真阅读主题文档,分析主题目录结构,了解每个文件是对应网页哪个部分的,认真阅读美化教程,美化教程本质上只为你提供核心代码和思路,具体代码要添加到哪个地方,需要你自己搞懂主题结构,添加到需要的、合适的位置! 博客美化终极奥秘:创作第一,体验第二,避免繁杂,简洁为上! 【01】添加评论系统 主流的评论系统有很多,比如:网易云跟帖、多说、友言、畅言、来必力(LiveRe)、Disqus、Valine、Gitment等等,目前网易云跟帖、多说、友言都已经关闭了,还有些可能需要翻墙,比较麻烦,百度了一下,最后还是选择了来必力评论系统 进入来必力官网,注册一个账号(注册时可能需要翻墙) 注册完毕之后,登录,进入安装页面,选择 City 免费版安装,安装之后你会得到一段代码 我们打开主题文件下的 _config.yml 文件,添加如下代码: 在 \\themes\\hexo-theme-spfk\\layout\\_partial\\comments 文件夹下新建一个 livere.ejs 的文件,在里面填写来必力提供的代码: 123456789101112131415161718<!-- 来必力City版安装代码 --><div id=\"lv-container\" data-id=\"city\" data-uid=\"这里是你的uid\"> <script type=\"text/javascript\"> (function(d, s) { var j, e = d.getElementsByTagName(s)[0]; if (typeof LivereTower === 'function') { return; } j = d.createElement(s); j.src = 'https://cdn-city.livere.com/js/embed.dist.js'; j.async = true; e.parentNode.insertBefore(j, e); })(document, 'script'); </script> <noscript>为正常使用来必力评论功能请激活JavaScript</noscript></div><!-- City版安装代码已完成 --> 打开 \\themes\\hexo-theme-spfk\\layout\\_partial\\article.ejs 文件,在适当位置添加如下红框中的代码: 完成以上操作之后,我们就可以使用来必力评论系统了 【02】添加卡通人物 我在逛别人博客的时候偶然发现右下角居然有一个萌萌的卡通人物,还能根据你鼠标位置摇头,瞬间被吸引到了,赶紧也给自己博客添加一个吧!点击此处进入该项目地址 输入如下命令获取 live2d : 1$ npm install --save hexo-helper-live2d 输入以下命令,下载相应的模型,将 packagename 更换成模型名称即可,更多模型选择请点击此处,各个模型的预览请访问原作者的博客 1$ npm install packagename 打开站点目录下的 _config.yml 文件,添加如下代码: 1234567891011live2d: enable: true scriptFrom: local model: use: live2d-widget-model-haruto #模型选择 display: position: right #模型位置 width: 150 #模型宽度 height: 300 #模型高度 mobile: show: false #是否在手机端显示 设置好过后我们就拥有了一个卡通人物 【03】自定义鼠标指针样式 在 \\themes\\material-x\\source\\less\\_base.less 文件 body 样式里写入如下代码: 123456body { cursor: url(https://cdn.jsdelivr.net/gh/TRHX/CDN-for-itrhx.com@2.1.6/images/mouse.cur),auto; background-color: @theme_background; ...... ......} 鼠标指针可以用 Axialis CursorWorkshop 这个软件自己制作,不同主题具体放的文件有所不同,确保在博客主体 body 的 CSS 文件中即可,其中的鼠标指针链接可替换成自己的,首先尝试加载 https://cdn.jsdelivr.net/gh/TRHX/CDN-for-itrhx.com@2.1.6/images/mouse.cur ,如果该文件不存在或由于其他原因无效,那么 auto 会被使用,也就是自动默认效果,图片格式为.ico、.ani、.cur,建议使用.cur,如果使用.ani或者其他格式无效,原因是浏览器兼容问题,请阅读参考文档或者参考以下兼容表: 浏览器 最低版本 格式 Internet Explorer 6.0 .cur / .ani Firefox (Gecko), Windows and Linux 1.5 (1.8) .cur / .png / .gif / .jpg Firefox (Gecko) 4.0 (2.0) .cur / .png / .gif / .jpg / .svg Opera — — Safari (Webkit) 3.0 (522-523) .cur / .png / .gif / .jpg 拓展阅读:《CSS 鼠标样式 cursor属性》 (By:歪脖先生的博客) 【04】添加鼠标点击爱心效果 在 \\themes\\hexo-theme-spfk\\source\\js 下新建文件 love.js,在 love.js 文件中添加以下代码: 1!function(e,t,a){function n(){c(\".heart{width: 10px;height: 10px;position: fixed;background: #f00;transform: rotate(45deg);-webkit-transform: rotate(45deg);-moz-transform: rotate(45deg);}.heart:after,.heart:before{content: '';width: inherit;height: inherit;background: inherit;border-radius: 50%;-webkit-border-radius: 500%;-moz-border-radius: 50%;position: fixed;}.heart:after{top: -5px;}.heart:before{left: -5px;}\"),o(),r()}function r(){for(var e=0;e<d.length;e++)d[e].alpha<=0?(t.body.removeChild(d[e].el),d.splice(e,1)):(d[e].y--,d[e].scale+=.004,d[e].alpha-=.013,d[e].el.style.cssText=\"left:\"+d[e].x+\"px;top:\"+d[e].y+\"px;opacity:\"+d[e].alpha+\";transform:scale(\"+d[e].scale+\",\"+d[e].scale+\") rotate(45deg);background:\"+d[e].color+\";z-index:99999\");requestAnimationFrame(r)}function o(){var t=\"function\"==typeof e.onclick&&e.onclick;e.onclick=function(e){t&&t(),i(e)}}function i(e){var a=t.createElement(\"div\");a.className=\"heart\",d.push({el:a,x:e.clientX-5,y:e.clientY-5,scale:1,alpha:1,color:s()}),t.body.appendChild(a)}function c(e){var a=t.createElement(\"style\");a.type=\"text/css\";try{a.appendChild(t.createTextNode(e))}catch(t){a.styleSheet.cssText=e}t.getElementsByTagName(\"head\")[0].appendChild(a)}function s(){return\"rgb(\"+~~(255*Math.random())+\",\"+~~(255*Math.random())+\",\"+~~(255*Math.random())+\")\"}var d=[];e.requestAnimationFrame=function(){return e.requestAnimationFrame||e.webkitRequestAnimationFrame||e.mozRequestAnimationFrame||e.oRequestAnimationFrame||e.msRequestAnimationFrame||function(e){setTimeout(e,1e3/60)}}(),n()}(window,document); 在 \\themes\\hexo-theme-spfk\\layout\\layout.ejs 文件末尾添加以下代码: 12<!-- 页面点击小红心 --><script type=\"text/javascript\" src=\"/js/love.js\"></script> 完成以上操作后,当我们点击鼠标的时候就可以看见爱心的特效了 【05】添加鼠标点击显示字体效果 在 \\themes\\hexo-theme-spfk\\source\\js 下新建文件 click_show_text.js,在 click_show_text.js 文件中添加以下代码: 123456789101112131415161718192021222324252627282930313233var a_idx = 0;jQuery(document).ready(function($) { $(\"body\").click(function(e) { var a = new Array (\"富强\", \"民主\", \"文明\", \"和谐\", \"自由\", \"平等\", \"公正\", \"法治\", \"爱国\", \"敬业\", \"诚信\", \"友善\"); var $i = $(\"<span/>\").text(a[a_idx]); a_idx = (a_idx + 1) % a.length; var x = e.pageX, y = e.pageY; $i.css({ \"z-index\": 5, \"top\": y - 20, \"left\": x, \"position\": \"absolute\", \"font-weight\": \"bold\", \"color\": \"#FF0000\" }); $(\"body\").append($i); $i.animate({ \"top\": y - 180, \"opacity\": 0 }, 3000, function() { $i.remove(); }); }); setTimeout('delay()', 2000);});function delay() { $(\".buryit\").removeAttr(\"onclick\");} 其中的社会主义核心价值观可以根据你自己的创意替换为其他文字 如果想要每次点击显示的文字为不同颜色,可以将其中 color 值进行如下更改: 1\"color\": \"rgb(\" + ~~(255 * Math.random()) + \",\" + ~~(255 * Math.random()) + \",\" + ~~(255 * Math.random()) + \")\" 然后在 \\themes\\hexo-theme-spfk\\layout\\layout.ejs 文件末尾添加以下代码: 12<!--单击显示文字--><script type=\"text/javascript\" src=\"/js/click_show_text.js\"></script> 最终实现效果如下: 【06】添加鼠标点击烟花爆炸效果 在 \\themes\\material-x\\source\\js 目录下新建一个 fireworks.js 的文件,里面写入以下代码: 1\"use strict\";function updateCoords(e){pointerX=(e.clientX||e.touches[0].clientX)-canvasEl.getBoundingClientRect().left,pointerY=e.clientY||e.touches[0].clientY-canvasEl.getBoundingClientRect().top}function setParticuleDirection(e){var t=anime.random(0,360)*Math.PI/180,a=anime.random(50,180),n=[-1,1][anime.random(0,1)]*a;return{x:e.x+n*Math.cos(t),y:e.y+n*Math.sin(t)}}function createParticule(e,t){var a={};return a.x=e,a.y=t,a.color=colors[anime.random(0,colors.length-1)],a.radius=anime.random(16,32),a.endPos=setParticuleDirection(a),a.draw=function(){ctx.beginPath(),ctx.arc(a.x,a.y,a.radius,0,2*Math.PI,!0),ctx.fillStyle=a.color,ctx.fill()},a}function createCircle(e,t){var a={};return a.x=e,a.y=t,a.color=\"#F00\",a.radius=0.1,a.alpha=0.5,a.lineWidth=6,a.draw=function(){ctx.globalAlpha=a.alpha,ctx.beginPath(),ctx.arc(a.x,a.y,a.radius,0,2*Math.PI,!0),ctx.lineWidth=a.lineWidth,ctx.strokeStyle=a.color,ctx.stroke(),ctx.globalAlpha=1},a}function renderParticule(e){for(var t=0;t<e.animatables.length;t++){e.animatables[t].target.draw()}}function animateParticules(e,t){for(var a=createCircle(e,t),n=[],i=0;i<numberOfParticules;i++){n.push(createParticule(e,t))}anime.timeline().add({targets:n,x:function(e){return e.endPos.x},y:function(e){return e.endPos.y},radius:0.1,duration:anime.random(1200,1800),easing:\"easeOutExpo\",update:renderParticule}).add({targets:a,radius:anime.random(80,160),lineWidth:0,alpha:{value:0,easing:\"linear\",duration:anime.random(600,800)},duration:anime.random(1200,1800),easing:\"easeOutExpo\",update:renderParticule,offset:0})}function debounce(e,t){var a;return function(){var n=this,i=arguments;clearTimeout(a),a=setTimeout(function(){e.apply(n,i)},t)}}var canvasEl=document.querySelector(\".fireworks\");if(canvasEl){var ctx=canvasEl.getContext(\"2d\"),numberOfParticules=30,pointerX=0,pointerY=0,tap=\"mousedown\",colors=[\"#FF1461\",\"#18FF92\",\"#5A87FF\",\"#FBF38C\"],setCanvasSize=debounce(function(){canvasEl.width=2*window.innerWidth,canvasEl.height=2*window.innerHeight,canvasEl.style.width=window.innerWidth+\"px\",canvasEl.style.height=window.innerHeight+\"px\",canvasEl.getContext(\"2d\").scale(2,2)},500),render=anime({duration:1/0,update:function(){ctx.clearRect(0,0,canvasEl.width,canvasEl.height)}});document.addEventListener(tap,function(e){\"sidebar\"!==e.target.id&&\"toggle-sidebar\"!==e.target.id&&\"A\"!==e.target.nodeName&&\"IMG\"!==e.target.nodeName&&(render.play(),updateCoords(e),animateParticules(pointerX,pointerY))},!1),setCanvasSize(),window.addEventListener(\"resize\",setCanvasSize,!1)}\"use strict\";function updateCoords(e){pointerX=(e.clientX||e.touches[0].clientX)-canvasEl.getBoundingClientRect().left,pointerY=e.clientY||e.touches[0].clientY-canvasEl.getBoundingClientRect().top}function setParticuleDirection(e){var t=anime.random(0,360)*Math.PI/180,a=anime.random(50,180),n=[-1,1][anime.random(0,1)]*a;return{x:e.x+n*Math.cos(t),y:e.y+n*Math.sin(t)}}function createParticule(e,t){var a={};return a.x=e,a.y=t,a.color=colors[anime.random(0,colors.length-1)],a.radius=anime.random(16,32),a.endPos=setParticuleDirection(a),a.draw=function(){ctx.beginPath(),ctx.arc(a.x,a.y,a.radius,0,2*Math.PI,!0),ctx.fillStyle=a.color,ctx.fill()},a}function createCircle(e,t){var a={};return a.x=e,a.y=t,a.color=\"#F00\",a.radius=0.1,a.alpha=0.5,a.lineWidth=6,a.draw=function(){ctx.globalAlpha=a.alpha,ctx.beginPath(),ctx.arc(a.x,a.y,a.radius,0,2*Math.PI,!0),ctx.lineWidth=a.lineWidth,ctx.strokeStyle=a.color,ctx.stroke(),ctx.globalAlpha=1},a}function renderParticule(e){for(var t=0;t<e.animatables.length;t++){e.animatables[t].target.draw()}}function animateParticules(e,t){for(var a=createCircle(e,t),n=[],i=0;i<numberOfParticules;i++){n.push(createParticule(e,t))}anime.timeline().add({targets:n,x:function(e){return e.endPos.x},y:function(e){return e.endPos.y},radius:0.1,duration:anime.random(1200,1800),easing:\"easeOutExpo\",update:renderParticule}).add({targets:a,radius:anime.random(80,160),lineWidth:0,alpha:{value:0,easing:\"linear\",duration:anime.random(600,800)},duration:anime.random(1200,1800),easing:\"easeOutExpo\",update:renderParticule,offset:0})}function debounce(e,t){var a;return function(){var n=this,i=arguments;clearTimeout(a),a=setTimeout(function(){e.apply(n,i)},t)}}var canvasEl=document.querySelector(\".fireworks\");if(canvasEl){var ctx=canvasEl.getContext(\"2d\"),numberOfParticules=30,pointerX=0,pointerY=0,tap=\"mousedown\",colors=[\"#FF1461\",\"#18FF92\",\"#5A87FF\",\"#FBF38C\"],setCanvasSize=debounce(function(){canvasEl.width=2*window.innerWidth,canvasEl.height=2*window.innerHeight,canvasEl.style.width=window.innerWidth+\"px\",canvasEl.style.height=window.innerHeight+\"px\",canvasEl.getContext(\"2d\").scale(2,2)},500),render=anime({duration:1/0,update:function(){ctx.clearRect(0,0,canvasEl.width,canvasEl.height)}});document.addEventListener(tap,function(e){\"sidebar\"!==e.target.id&&\"toggle-sidebar\"!==e.target.id&&\"A\"!==e.target.nodeName&&\"IMG\"!==e.target.nodeName&&(render.play(),updateCoords(e),animateParticules(pointerX,pointerY))},!1),setCanvasSize(),window.addEventListener(\"resize\",setCanvasSize,!1)}; 然后在 \\themes\\material-x\\layout\\layout.ejs 文件中写入以下代码: 123<canvas class=\"fireworks\" style=\"position: fixed;left: 0;top: 0;z-index: 1; pointer-events: none;\" ></canvas> <script type=\"text/javascript\" src=\"//cdn.bootcss.com/animejs/2.2.0/anime.min.js\"></script> <script type=\"text/javascript\" src=\"/js/fireworks.js\"></script> 最终效果: 【07】添加彩色滚动变换字体 在你想要添加彩色滚动变换字体的地方写入以下代码即可,其中文字可自行更改: 123456789101112131415161718192021222324252627282930313233343536373839404142<div id=\"binft\"></div> <script> var binft = function (r) { function t() { return b[Math.floor(Math.random() * b.length)] } function e() { return String.fromCharCode(94 * Math.random() + 33) } function n(r) { for (var n = document.createDocumentFragment(), i = 0; r > i; i++) { var l = document.createElement(\"span\"); l.textContent = e(), l.style.color = t(), n.appendChild(l) } return n } function i() { var t = o[c.skillI]; c.step ? c.step-- : (c.step = g, c.prefixP < l.length ? (c.prefixP >= 0 && (c.text += l[c.prefixP]), c.prefixP++) : \"forward\" === c.direction ? c.skillP < t.length ? (c.text += t[c.skillP], c.skillP++) : c.delay ? c.delay-- : (c.direction = \"backward\", c.delay = a) : c.skillP > 0 ? (c.text = c.text.slice(0, -1), c.skillP--) : (c.skillI = (c.skillI + 1) % o.length, c.direction = \"forward\")), r.textContent = c.text, r.appendChild(n(c.prefixP < l.length ? Math.min(s, s + c.prefixP) : Math.min(s, t.length - c.skillP))), setTimeout(i, d) } var l = \"\", o = [\"青青陵上柏,磊磊涧中石。\", \"人生天地间,忽如远行客。\",\"斗酒相娱乐,聊厚不为薄。\", \"驱车策驽马,游戏宛与洛。\",\"洛中何郁郁,冠带自相索。\",\"长衢罗夹巷,王侯多第宅。\",\"两宫遥相望,双阙百余尺。\",\"极宴娱心意,戚戚何所迫?\"].map(function (r) { return r + \"\" }), a = 2, g = 1, s = 5, d = 75, b = [\"rgb(110,64,170)\", \"rgb(150,61,179)\", \"rgb(191,60,175)\", \"rgb(228,65,157)\", \"rgb(254,75,131)\", \"rgb(255,94,99)\", \"rgb(255,120,71)\", \"rgb(251,150,51)\", \"rgb(226,183,47)\", \"rgb(198,214,60)\", \"rgb(175,240,91)\", \"rgb(127,246,88)\", \"rgb(82,246,103)\", \"rgb(48,239,130)\", \"rgb(29,223,163)\", \"rgb(26,199,194)\", \"rgb(35,171,216)\", \"rgb(54,140,225)\", \"rgb(76,110,219)\", \"rgb(96,84,200)\"], c = { text: \"\", prefixP: -s, skillI: 0, skillP: 0, direction: \"forward\", delay: a, step: g }; i() }; binft(document.getElementById('binft')); </script> 最终效果: 【08】添加字数统计和阅读时长 先在博客目录下执行以下命令安装 hexo-wordcount 插件: 1$ npm i --save hexo-wordcount 注意:在 Material X 主题中,字数统计和阅读时长的功能我已提交 PR,在最新版本中,只需要安装插件后,在主题 config.yml 配置文件里,将 word_count 关键字设置为 true 即可,对于旧版本,可以通过以下方法实现: 以 Material X 主题(版本 1.2.1)为例,在 \\themes\\material-x\\layout\\_meta 目录下创建 word.ejs 文件,在 word.ejs 文件中写入以下代码: 123456789101112131415161718192021<% if(isPostList || !isPostList){ %> <% if (theme.word_count && !post.no_word_count) { %> <div style=\"margin-right: 10px;\"> <span class=\"post-time\"> <span class=\"post-meta-item-icon\"> <i class=\"fa fa-keyboard\"></i> <span class=\"post-meta-item-text\"> 字数统计: </span> <span class=\"post-count\"><%= wordcount(post.content) %>字</span> </span> </span> &nbsp; | &nbsp; <span class=\"post-time\"> <span class=\"post-meta-item-icon\"> <i class=\"fa fa-hourglass-half\"></i> <span class=\"post-meta-item-text\"> 阅读时长≈</span> <span class=\"post-count\"><%= min2read(post.content) %>分</span> </span> </span> </div> <% } %><% } %> 然后在主题的配置文件 _config.yml 找到 meta 关键字,将 word 填入 header 中: 123meta: header: [title, author, date, categories, tags, counter, word, top] footer: [updated, share] 最后在主题目录下的 _config.yml 添加以下配置即可 1word_count: true 效果图: 同样的,以 spfk 主题为例,在 \\themes\\hexo-theme-spfk\\layout\\_partial\\post 目录下创建 word.ejs 文件,在 word.ejs 文件中写入以下代码: 1234567891011121314151617<div style=\"margin-top:10px;\"> <span class=\"post-time\"> <span class=\"post-meta-item-icon\"> <i class=\"fa fa-keyboard-o\"></i> <span class=\"post-meta-item-text\"> 字数统计: </span> <span class=\"post-count\"><%= wordcount(post.content) %>字</span> </span> </span> &nbsp; | &nbsp; <span class=\"post-time\"> <span class=\"post-meta-item-icon\"> <i class=\"fa fa-hourglass-half\"></i> <span class=\"post-meta-item-text\"> 阅读时长: </span> <span class=\"post-count\"><%= min2read(post.content) %>分</span> </span> </span></div> 然后在 \\themes\\hexo-theme-spfk\\layout\\_partial\\article.ejs 中适当位置添加以下代码: 最后在主题目录下的 _config.yml 添加以下配置 1word_count: true 如果显示的位置不好,可以自行更改其位置,成功配置后的效果如下: 另外:要在博客底部显示所有文章的总字数,可以点击此处,根据你博客底部文件的类型选择相应的代码放在适当的位置即可,前提是要安装好 hexo-wordcount 插件,例如我使用 Material X 主题,在 \\themes\\material-x\\layout\\_partial 目录下的 footer.ejs 文件中添加如下代码: 12<i class=\"fas fa-chart-area\"></i><span class=\"post-count\">字数统计:<%= totalcount(site) %></span> 实现效果如下: 【09】添加背景音乐 打开网页版网易云音乐,选择你准备添加的背景音乐,点击生成外链播放器,前提是要有版权,不然是无法生成外链播放器的,复制底下的HTML代码 然后将此代码放到你想要放的地方,比如放在博客的左侧,则打开 \\themes\\hexo-theme-spfk\\layout\\_partial\\left-col.ejs 文件,将复制的HTML代码粘贴进去,再进行适当的位置设置让播放器更美观,其中 auto=1 表示打开网页自动播放音乐,auto=0 表示关闭自动播放音乐 最后效果如下: 这种网易云音乐外链的方式有很多局限性,因此推荐使用aplayer,GitHub地址为:https://github.com/MoePlayer/APlayer ,参考教程:《hexo上的aplayer应用》 【10】添加网站运行时间 一个比较好的小功能,可以看见自己的博客运行多久了,时间一天天的增加,成就感也会一天天增加的在 \\themes\\hexo-theme-spfk\\layout\\_partial\\footer.ejs 文件下添加以下代码: 1234567891011121314151617<span id=\"timeDate\">载入天数...</span><span id=\"times\">载入时分秒...</span><script> var now = new Date(); function createtime() { var grt= new Date(\"08/10/2018 17:38:00\");//在此处修改你的建站时间,格式:月/日/年 时:分:秒 now.setTime(now.getTime()+250); days = (now - grt ) / 1000 / 60 / 60 / 24; dnum = Math.floor(days); hours = (now - grt ) / 1000 / 60 / 60 - (24 * dnum); hnum = Math.floor(hours); if(String(hnum).length ==1 ){hnum = \"0\" + hnum;} minutes = (now - grt ) / 1000 /60 - (24 * 60 * dnum) - (60 * hnum); mnum = Math.floor(minutes); if(String(mnum).length ==1 ){mnum = \"0\" + mnum;} seconds = (now - grt ) / 1000 - (24 * 60 * 60 * dnum) - (60 * 60 * hnum) - (60 * mnum); snum = Math.round(seconds); if(String(snum).length ==1 ){snum = \"0\" + snum;} document.getElementById(\"timeDate\").innerHTML = \"本站已安全运行 \"+dnum+\" 天 \"; document.getElementById(\"times\").innerHTML = hnum + \" 小时 \" + mnum + \" 分 \" + snum + \" 秒\"; } setInterval(\"createtime()\",250);</script> 最后效果如下: 【11】添加百度统计 百度统计是百度推出的一款免费的专业网站流量分析工具,能够告诉用户访客是如何找到并浏览用户的网站,在网站上做了些什么,非常有趣,接下来我们把百度统计添加到自己博客当中 访问百度统计首页,注册一个账号后登陆,添加你的博客网站 接着点击代码获取,复制该代码 然后到目录 \\Hexo\\themes\\hexo-theme-spfk\\layout\\_partial 下新建一个 baidu-analytics.ejs 文件,里面粘贴你刚刚复制的代码 修改主题文件夹下的 _config.yml 文件,将你的key(图中涂掉部分)填写进去: 所有操作完成后可以在百度统计管理页面检查代码是否安装成功,如果代码安装正确,一般20分钟后,可以查看网站分析数据 另外推荐:友盟,2010年4月在北京成立,安全、可靠、公正、第三方的网站流量统计分析系统 【12】浏览器网页标题恶搞当用户访问你的博客时点击到了其他网页,我们可以恶搞一下网页标题,呼唤用户回来,首先在目录 \\themes\\material-x\\source\\js 下新建一个 FunnyTitle.js 文件,在里面填写如下代码: 1234567891011121314151617// 浏览器搞笑标题var OriginTitle = document.title;var titleTime;document.addEventListener('visibilitychange', function () { if (document.hidden) { $('[rel=\"icon\"]').attr('href', \"/funny.ico\"); document.title = '╭(°A°`)╮ 页面崩溃啦 ~'; clearTimeout(titleTime); } else { $('[rel=\"icon\"]').attr('href', \"/favicon.ico\"); document.title = '(ฅ>ω<*ฅ) 噫又好啦 ~' + OriginTitle; titleTime = setTimeout(function () { document.title = OriginTitle; }, 2000); }}); 其中 funny.ico 是用户切换到其他标签后你网站的图标,favicon.ico 是正常图标,然后在 \\themes\\material-x\\layout\\layout.ejs 文件中添加如下代码: 12<!--浏览器搞笑标题--><script type=\"text/javascript\" src=\"/js/FunnyTitle.js\"></script> 再次部署博客后就可以看见标题搞笑的效果了: 【13】背景添加动态线条效果 在 \\Hexo\\themes\\hexo-theme-spfk\\layout\\layout.ejs 文件中添加如下代码: 1234<!--动态线条背景--><script type=\"text/javascript\"color=\"220,220,220\" opacity='0.7' zIndex=\"-2\" count=\"200\" src=\"//cdn.bootcss.com/canvas-nest.js/1.0.0/canvas-nest.min.js\"></script> 其中: color:表示线条颜色,三个数字分别为(R,G,B),默认:(0,0,0) opacity:表示线条透明度(0~1),默认:0.5 count:表示线条的总数量,默认:150 zIndex:表示背景的z-index属性,css属性用于控制所在层的位置,默认:-1 最终实现效果: 【14】添加人体时钟 无意中发现了个有趣的人体时钟 HONE HONE CLOCK,作者是个日本人,点击此处访问作者博客,点击此处在作者原博客上查看动态样式,点击此处查看动态大图,如果你的博客上有合适的地方,加上一个人体时钟会很有趣的 实现代码: 12345<!--人体时钟背景透明--><script charset=\"Shift_JIS\" src=\"http://chabudai.sakura.ne.jp/blogparts/honehoneclock/honehone_clock_tr.js\"></script><!--人体时钟背景白--><script charset=\"Shift_JIS\" src=\"http://chabudai.sakura.ne.jp/blogparts/honehoneclock/honehone_clock_wh.js\"></script> 其他网页小挂件推荐: http://abowman.com/ 里面有很多有趣的小挂件,可以养养鱼、龟、狗、仓鼠等各式各样的虚拟宠物,能根据你的鼠标指针位置移动,直接复制代码就可以用 http://www.revolvermaps.com/ 它提供网站访客地理信息,可以以2D、3D等形式显示 http://www.amazingcounters.com/ 免费网站计数器,有非常多的样式供你选择,可以设置计数器初始数值,可以设置按访问量计数,也可以按独立访问者计数 https://www.seniverse.com/widget/get 心知天气提供基于Web的免费天气插件,可以为你的网站添加一项简洁美观的天气预报功能,并自动适配PC和手机上的浏览 【15】添加RSS订阅 RSS订阅是站点用来和其他站点之间共享内容的一种简易方式,即Really Simple Syndication(简易信息聚合),如果不会使用,可以参见百度百科:https://baike.baidu.com/item/RSS%E8%AE%A2%E9%98%85/663114 ;首先我们安装feed插件,在本地hexo目录下右键git bash here,输入以下命令: 1$ npm install hexo-generator-feed 等待安装完成后,打开hexo目录下配置文件的_config.yml,在末尾添加以下配置: 12345678910# Extensions## Plugins: http://hexo.io/plugins/#RSS订阅plugin:- hexo-generator-feed#Feed Atomfeed:type: atompath: atom.xmllimit: 20 随后打开主题配置文件_config.yml,添加以下配置: 1rss: /atom.xml 至此,RSS订阅功能添加完成 【16】添加网站雪花飘落效果 样式一和样式二分别如下: 实现方法:在 \\Hexo\\themes\\hexo-theme-spfk\\source\\js 目录下新建一个 snow.js 文件,粘贴以下代码: 123456789101112131415161718192021222324252627282930313233343536373839404142/*样式一*/(function($){ $.fn.snow = function(options){ var $flake = $('<div id=\"snowbox\" />').css({'position': 'absolute','z-index':'9999', 'top': '-50px'}).html('&#10052;'), documentHeight = $(document).height(), documentWidth = $(document).width(), defaults = { minSize : 10, maxSize : 20, newOn : 1000, flakeColor : \"#AFDAEF\" /* 此处可以定义雪花颜色,若要白色可以改为#FFFFFF */ }, options = $.extend({}, defaults, options); var interval= setInterval( function(){ var startPositionLeft = Math.random() * documentWidth - 100, startOpacity = 0.5 + Math.random(), sizeFlake = options.minSize + Math.random() * options.maxSize, endPositionTop = documentHeight - 200, endPositionLeft = startPositionLeft - 500 + Math.random() * 500, durationFall = documentHeight * 10 + Math.random() * 5000; $flake.clone().appendTo('body').css({ left: startPositionLeft, opacity: startOpacity, 'font-size': sizeFlake, color: options.flakeColor }).animate({ top: endPositionTop, left: endPositionLeft, opacity: 0.2 },durationFall,'linear',function(){ $(this).remove() }); }, options.newOn); };})(jQuery);$(function(){ $.fn.snow({ minSize: 5, /* 定义雪花最小尺寸 */ maxSize: 50,/* 定义雪花最大尺寸 */ newOn: 300 /* 定义密集程度,数字越小越密集 */ });}); 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128/*样式二*//* 控制下雪 */function snowFall(snow) { /* 可配置属性 */ snow = snow || {}; this.maxFlake = snow.maxFlake || 200; /* 最多片数 */ this.flakeSize = snow.flakeSize || 10; /* 雪花形状 */ this.fallSpeed = snow.fallSpeed || 1; /* 坠落速度 */}/* 兼容写法 */requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame || window.oRequestAnimationFrame || function(callback) { setTimeout(callback, 1000 / 60); };cancelAnimationFrame = window.cancelAnimationFrame || window.mozCancelAnimationFrame || window.webkitCancelAnimationFrame || window.msCancelAnimationFrame || window.oCancelAnimationFrame;/* 开始下雪 */snowFall.prototype.start = function(){ /* 创建画布 */ snowCanvas.apply(this); /* 创建雪花形状 */ createFlakes.apply(this); /* 画雪 */ drawSnow.apply(this)}/* 创建画布 */function snowCanvas() { /* 添加Dom结点 */ var snowcanvas = document.createElement(\"canvas\"); snowcanvas.id = \"snowfall\"; snowcanvas.width = window.innerWidth; snowcanvas.height = document.body.clientHeight; snowcanvas.setAttribute(\"style\", \"position:absolute; top: 0; left: 0; z-index: 1; pointer-events: none;\"); document.getElementsByTagName(\"body\")[0].appendChild(snowcanvas); this.canvas = snowcanvas; this.ctx = snowcanvas.getContext(\"2d\"); /* 窗口大小改变的处理 */ window.onresize = function() { snowcanvas.width = window.innerWidth; /* snowcanvas.height = window.innerHeight */ }}/* 雪运动对象 */function flakeMove(canvasWidth, canvasHeight, flakeSize, fallSpeed) { this.x = Math.floor(Math.random() * canvasWidth); /* x坐标 */ this.y = Math.floor(Math.random() * canvasHeight); /* y坐标 */ this.size = Math.random() * flakeSize + 2; /* 形状 */ this.maxSize = flakeSize; /* 最大形状 */ this.speed = Math.random() * 1 + fallSpeed; /* 坠落速度 */ this.fallSpeed = fallSpeed; /* 坠落速度 */ this.velY = this.speed; /* Y方向速度 */ this.velX = 0; /* X方向速度 */ this.stepSize = Math.random() / 30; /* 步长 */ this.step = 0 /* 步数 */}flakeMove.prototype.update = function() { var x = this.x, y = this.y; /* 左右摆动(余弦) */ this.velX *= 0.98; if (this.velY <= this.speed) { this.velY = this.speed } this.velX += Math.cos(this.step += .05) * this.stepSize; this.y += this.velY; this.x += this.velX; /* 飞出边界的处理 */ if (this.x >= canvas.width || this.x <= 0 || this.y >= canvas.height || this.y <= 0) { this.reset(canvas.width, canvas.height) }};/* 飞出边界-放置最顶端继续坠落 */flakeMove.prototype.reset = function(width, height) { this.x = Math.floor(Math.random() * width); this.y = 0; this.size = Math.random() * this.maxSize + 2; this.speed = Math.random() * 1 + this.fallSpeed; this.velY = this.speed; this.velX = 0;};// 渲染雪花-随机形状(此处可修改雪花颜色!!!)flakeMove.prototype.render = function(ctx) { var snowFlake = ctx.createRadialGradient(this.x, this.y, 0, this.x, this.y, this.size); snowFlake.addColorStop(0, \"rgba(255, 255, 255, 0.9)\"); /* 此处是雪花颜色,默认是白色 */ snowFlake.addColorStop(.5, \"rgba(255, 255, 255, 0.5)\"); /* 若要改为其他颜色,请自行查 */ snowFlake.addColorStop(1, \"rgba(255, 255, 255, 0)\"); /* 找16进制的RGB 颜色代码。 */ ctx.save(); ctx.fillStyle = snowFlake; ctx.beginPath(); ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2); ctx.fill(); ctx.restore();};/* 创建雪花-定义形状 */function createFlakes() { var maxFlake = this.maxFlake, flakes = this.flakes = [], canvas = this.canvas; for (var i = 0; i < maxFlake; i++) { flakes.push(new flakeMove(canvas.width, canvas.height, this.flakeSize, this.fallSpeed)) }}/* 画雪 */function drawSnow() { var maxFlake = this.maxFlake, flakes = this.flakes; ctx = this.ctx, canvas = this.canvas, that = this; /* 清空雪花 */ ctx.clearRect(0, 0, canvas.width, canvas.height); for (var e = 0; e < maxFlake; e++) { flakes[e].update(); flakes[e].render(ctx); } /* 一帧一帧的画 */ this.loop = requestAnimationFrame(function() { drawSnow.apply(that); });}/* 调用及控制方法 */var snow = new snowFall({maxFlake:60});snow.start(); 然后在 \\Hexo\\themes\\hexo-theme-spfk\\layout\\layout.ejs 文件里引用即可: 12<!-- 雪花特效 --><script type=\"text/javascript\" src=\"\\js\\snow.js\"></script> 如果没效果,请确认网页是否已载入JQurey,如果没有请在下雪代码之前引入JQ即可: 12<script type=\"text/javascript\" src=\"http://libs.baidu.com/jquery/1.8.3/jquery.js\"></script><script type=\"text/javascript\" src=\"http://libs.baidu.com/jquery/1.8.3/jquery.min.js\"></script> 原文链接:《分享两种圣诞节雪花特效JS代码(网站下雪效果)》 【17】添加 Fork me on GitHub 效果 效果图: 点击此处可以查看更多样式,将相应样式的代码复制到你想要放的地方就OK了,代码里的链接也要替换成你的,更多创意,比如 Follow me on CSDN ,只需要用PS改掉图片里的文字,替换掉相应链接即可 【18】添加背景动态彩带效果 样式一是鼠标点击后彩带自动更换样式,样式二是飘动的彩带: 实现方法:在 \\themes\\material-x\\layout\\layout.ejs 文件的body前面添加如下代码: 12<!-- 样式一(鼠标点击更换样式) --><script src=\"https://g.joyinshare.com/hc/ribbon.min.js\" type=\"text/javascript\"></script> 12<!-- 样式二(飘动的彩带) --><script src=\"https://g.joyinshare.com/hc/piao.js\" type=\"text/javascript\"></script> 【19】添加背景代码雨特效 新建 DigitalRain.js,写入以下代码: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657window.onload = function(){ //获取画布对象 var canvas = document.getElementById(\"canvas\"); //获取画布的上下文 var context =canvas.getContext(\"2d\"); var s = window.screen; var W = canvas.width = s.width; var H = canvas.height; //获取浏览器屏幕的宽度和高度 //var W = window.innerWidth; //var H = window.innerHeight; //设置canvas的宽度和高度 canvas.width = W; canvas.height = H; //每个文字的字体大小 var fontSize = 12; //计算列 var colunms = Math.floor(W /fontSize); //记录每列文字的y轴坐标 var drops = []; //给每一个文字初始化一个起始点的位置 for(var i=0;i<colunms;i++){ drops.push(0); } //运动的文字 var str =\"WELCOME TO WWW.ITRHX.COM\"; //4:fillText(str,x,y);原理就是去更改y的坐标位置 //绘画的函数 function draw(){ context.fillStyle = \"rgba(238,238,238,.08)\";//遮盖层 context.fillRect(0,0,W,H); //给字体设置样式 context.font = \"600 \"+fontSize+\"px Georgia\"; //给字体添加颜色 context.fillStyle = [\"#33B5E5\", \"#0099CC\", \"#AA66CC\", \"#9933CC\", \"#99CC00\", \"#669900\", \"#FFBB33\", \"#FF8800\", \"#FF4444\", \"#CC0000\"][parseInt(Math.random() * 10)];//randColor();可以rgb,hsl, 标准色,十六进制颜色 //写入画布中 for(var i=0;i<colunms;i++){ var index = Math.floor(Math.random() * str.length); var x = i*fontSize; var y = drops[i] *fontSize; context.fillText(str[index],x,y); //如果要改变时间,肯定就是改变每次他的起点 if(y >= canvas.height && Math.random() > 0.99){ drops[i] = 0; } drops[i]++; } }; function randColor(){//随机颜色 var r = Math.floor(Math.random() * 256); var g = Math.floor(Math.random() * 256); var b = Math.floor(Math.random() * 256); return \"rgb(\"+r+\",\"+g+\",\"+b+\")\"; } draw(); setInterval(draw,35);}; 在主题文件的相关css文件中(以 Material X 1.2.1 主题为例,在\\themes\\material-x-1.2.1\\source\\less\\_main.less 文件末尾)添加以下代码: 12345678910canvas { position: fixed; right: 0px; bottom: 0px; min-width: 100%; min-height: 100%; height: auto; width: auto; z-index: -1;} 然后在主题的 layout.ejs 文件中引入即可: 123<!-- 数字雨 --><canvas id=\"canvas\" width=\"1440\" height=\"900\" ></canvas><script type=\"text/javascript\" src=\"/js/DigitalRain.js\"></script> 最终效果: 代码来源:http://www.lxl8800.cn/Main/Resource 【20】自定义一个不使用主题模板渲染的独立页面     有时候我们需要新建一个独立的页面,这个页面不使用主题的渲染,具有自己独立的样式,可以放一些自己的作品,相册什么的,以下就介绍这种独立页面的实现方法。 方法一:     使用 Hexo 提供的跳过渲染配置,在博客根目录的配置文件 _config.yml 里找到 skip_render 关键字,在后面添加想要跳过渲染的页面,比如我们创建 \\source\\about\\index.html, 配置文件填写:skip_render: about\\**,那么就表示 \\source\\about 里所有的文件将跳过渲染,里面的文件将会被直接复制到 public 文件夹,此时就会得到一个独立的 about 页面;官方文档:https://hexo.io/docs/configuration 方法二:     在文章头部的 Front-matter 里添加配置 layout: false 来跳过渲染配置,比如我们要使 about 页面跳过渲染,创建 \\source\\about\\index.md,将这个页面的相关 HTML 代码写进.md文件并保存,然后在 index.md 的头部写入: 123456789---layout: false---{% raw %}这里是 HTML 代码{% endraw %} PS:Front-matter 是 .md 文件最上方以 — 分隔的区域,用于指定个别文件的变量,官方文档:https://hexo.io/docs/front-matter 效果可以对比我的博客主页和关于页面 【21】更改本地预览端口号hexo博客在执行 hexo s 进行本地预览的时候,默认端口号是4000,当该端口号被占用时会报错 Error: listen EADDRINUSE 0.0.0.0:4000 ,此时可以关闭占用该端口的进程,也可以更换端口号,更换端口号可以通过以下两种方法实现: 方法一:在根目录的 _config.yml 配置文件内加上如下代码更改 hexo s 运行时的端口号: 1234server: port: 5000 compress: true header: true 方法二:通过 hexo server -p 5000 命令来指定端口,这种方法只是本次执行有效 未完待续……","categories":[{"name":"Hexo","slug":"Hexo","permalink":"https://www.itrhx.com/categories/Hexo/"}],"tags":[{"name":"Hexo","slug":"Hexo","permalink":"https://www.itrhx.com/tags/Hexo/"},{"name":"主题个性化","slug":"主题个性化","permalink":"https://www.itrhx.com/tags/主题个性化/"},{"name":"Material X","slug":"Material-X","permalink":"https://www.itrhx.com/tags/Material-X/"},{"name":"spfk","slug":"spfk","permalink":"https://www.itrhx.com/tags/spfk/"}]},{"title":"Markdown 语法&技巧总结","slug":"A03-markdown","date":"2018-08-25T09:57:16.879Z","updated":"2020-03-14T05:26:44.635Z","comments":true,"path":"2018/08/25/A03-markdown/","link":"","permalink":"https://www.itrhx.com/2018/08/25/A03-markdown/","excerpt":"","text":"在写博客的时候,我们不希望都是千篇一律的没有色彩,多了解一些 Markdown 语法技巧有利于丰富我们的博客,看起来更有 feel ! – 插入图片 如果你使用 MarkdownPad 的话就比较方便,可以直接选择插入本地图片或者是网络图片,实质是通过以下代码实现的,小括号里面就是你的图片地址,中括号里面是图片的替代文字,比如上面的图片代码如下:1![车](https://cdn.jsdelivr.net/gh/TRHX/ImageHosting/ITRHX-PIC/A03/01.jpg) – 插入音乐 打开网页版网易云音乐,选择你准备插入的音乐,点击生成外链播放器,前提是要有版权,不然是无法生成外链播放器的,选择好尺寸后,复制底下的HTML代码 然后将此HTML代码粘贴到你想要放的地方,可自行调节播放器的大小,其中 auto=1 表示打开网页自动播放音乐,auto=0 表示关闭自动播放音乐,比如See You Again (中英文版) - 罗艺恒这首歌曲代码如下: 1<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"//music.163.com/outchain/player?type=2&id=32405683&auto=1&height=66\"></iframe> – 插入视频 高考毕业了我们为下一届的学弟学妹们录制高考加油视频,我担任后期制作,在这里就以该视频为例٩(๑❛ᴗ❛๑)۶,在腾讯视频播放页面找到分享按钮,复制该视频的通用代码(其他视频播放平台也一样),粘贴到文章中对应位置即可,可根据情况调整视频播放器的大小 1<iframe frameborder=\"0\" width=\"840\" height=\"500\" src=\"https://v.qq.com/txp/iframe/player.html?vid=x0643zvgtf7\" allowFullScreen=\"true\"></iframe> 未完待续……","categories":[{"name":"Markdown","slug":"Markdown","permalink":"https://www.itrhx.com/categories/Markdown/"}],"tags":[{"name":"Markdown","slug":"Markdown","permalink":"https://www.itrhx.com/tags/Markdown/"},{"name":"技巧","slug":"技巧","permalink":"https://www.itrhx.com/tags/技巧/"}]},{"title":"使用 Github Pages 和 Hexo 搭建自己的独立博客","slug":"A02-hexo-blog","date":"2018-08-15T13:34:58.325Z","updated":"2020-04-26T04:01:45.694Z","comments":true,"path":"2018/08/15/A02-hexo-blog/","link":"","permalink":"https://www.itrhx.com/2018/08/15/A02-hexo-blog/","excerpt":"","text":"加入 Hexo 博客交流群:924812033,有问题互相交流学习! – 前言 首先感谢您能访问我的博客:TRHX’S BLOG 这是一篇有关如何使用 Github Pages 和 Hexo 搭建属于自己独立博客的详尽教程,本人是软件工程专业本科生,目前只学习了C和C++编程语言,对网站开发的有关知识几乎为零,这也是我搭建好自己的博客之后写的第一篇博客,刚开始搭建博客的时候自己也是网上各种百度,由于自己属于小白那种,历经了千辛万苦才弄好,所以借这个机会写一篇小白真正能看懂的博客搭建教程,教你一步一步走向成功的彼岸! 推荐文章: 《我为什么写博客》 (By 知明所以) 《为什么你应该(从现在开始就)写博客》 (By 刘未鹏 | Mind Hacks) – 入门 Github Pages Github Pages可以被认为是用户编写的、托管在github上的静态网页。使用Github Pages可以为你提供一个免费的服务器,免去了自己搭建服务器和写数据库的麻烦。此外还可以绑定自己的域名。 Hexo Hexo 是一个快速、简洁且高效的博客框架。Hexo 使用 Markdown(或其他渲染引擎)解析文章,在几秒内,即可利用靓丽的主题生成静态网页。 – 安装 Node.js点击此处访问官网,按需下载相应版本,默认安装可以了 注:本人在安装过程中出现了Warning 1909,无法创建快捷方式,这种情况很少出现,如果在安装过程中也有这种情况请参考百度文库(win10系统实测可行):《Win7安装程序警告1909无法创建快捷方式》 – 安装 Git点击此处访问官网,按需下载相应版本,默认安装即可参考资料:《如何在windows下安装GIT》 (By 俊雨廷休) 《Pro Git(中文版)》 – 检验软件是否安装成功同时按下 Win 键和 R 键打开运行窗口,输入 cmd ,然后输入以下命令,有相应版本信息显示则安装成功,若不正确可以卸载软件重新安装,此外若安装成功,在桌面右键鼠标,可以看到菜单里多了 Git GUI Here 和 Git Bash Here两个选项,第一个是图形界面的Git操作,另一个是命令行123$ git --version$ node -v$ npm -v – Hexo 安装选择一个磁盘,新建一个文件夹,自己重命名文件夹(如:我的文件夹为:E\\TRHX_Blog),博客相关文件将储存在此文件夹下,在该文件夹下右键鼠标,点击 Git Bash Here,输入以下 npm 命令即可安装,第一个命令表示安装 hexo,第二个命令表示安装 hexo 部署到 git page 的 deployer,如图所示即为安装成功12$ npm install hexo-cli -g$ npm install hexo-deployer-git --save – Hexo 初始化配置在刚才新建的文件夹里面再次新建一个 Hexo 文件夹(如:我的文件夹为:E\\TRHX_Blog\\Hexo),进入该 Hexo 文件夹右键鼠标,点击 Git Bash Here,输入以下命令,如图所示则安装成功1$ hexo init Hexo 安装完成后,将会在指定文件夹中新建所需要的文件,Hexo 文件夹下的目录如下: – 本地查看效果执行以下命令,执行完即可登录 http://localhost:4000/ 查看效果12$ hexo generate$ hexo server 显示以下信息说明操作成功:1INFO Hexo is running at http://0.0.0.0:4000/. Press Ctrl+C to stop. 登录 http://localhost:4000/ 查看效果: – 将博客部署到 Github Pages 上到目前为止,我们的本地博客就成功搭建了,但是现在我们只能通过本地连接查看博客,我们要做的是让其他人也能够访问我们的博客,这就需要我们将博客部署到Github Pages上 一、注册 Github 账户:点击此处访问 Github 官网,点击 Sign Up 注册账户 二、创建项目代码库:点击 New repository 开始创建,步骤及注意事项见图: 三、配置 SSH 密钥:只有配置好 SSH 密钥后,我们才可以通过 git 操作实现本地代码库与 Github 代码库同步,在你第一次新建的文件夹里面(如:我的文件夹为:E\\TRHX_Blog) Git Bash Here 输入以下命令:12$ ssh-keygen -t rsa -C "your email@example.com"//引号里面填写你的邮箱地址,比如我的是tanrenhou@126.com 之后会出现:123Generating public/private rsa key pair.Enter file in which to save the key (/c/Users/you/.ssh/id_rsa)://到这里可以直接回车将密钥按默认文件进行存储 然后会出现:123Enter passphrase (empty for no passphrase)://这里是要你输入密码,其实不需要输什么密码,直接回车就行Enter same passphrase again: 接下来屏幕会显示:123456Your identification has been saved in /c/Users/you/.ssh/id_rsa.Your public key has been saved in /c/Users/you/.ssh/id_rsa.pub.The key fingerprint is:这里是各种字母数字组成的字符串,结尾是你的邮箱The key's randomart image is:这里也是各种字母数字符号组成的字符串 运行以下命令,将公钥的内容复制到系统粘贴板上1$ clip < ~/.ssh/id_rsa.pub 四、在 GitHub 账户中添加你的公钥 1.登陆 GitHub,进入 Settings: 2.点击 SSH and GPG Keys: 3.选择 New SSH key: 4.粘贴密钥: 五、测试 输入以下命令:注意:git@github.com不要做任何更改!1$ ssh -T git@github.com 之后会显示: 输入 yes 后会显示:此时表示设置正确 六、配置 Git 个人信息 Git 会根据用户的名字和邮箱来记录提交,GitHub 也是用这些信息来做权限的处理,输入以下命令进行个人信息的设置,把名称和邮箱替换成你自己的,名字可以不是 GitHub 的昵称,但为了方便记忆,建议与 GitHub 一致12$ git config --global user.name "此处填你的用户名"$ git config --global user.email "此处填你的邮箱" 到此为止 SSH Key 配置成功,本机已成功连接到 Github – 将本地的 Hexo 文件更新到 Github 的库中一、登录 Github 打开自己的项目 yourname.github.io 二、鼠标移到 Clone or download 按钮,选择 Use SSH 三、一键复制地址 四、打开你创建的 Hexo 文件夹(如:E:\\TRHX_Blog\\Hexo),右键用记事本(或者Notepad++、Vs Code等)打开该文件夹下的 _config.yml 文件 五、按下图修改 _config.yml 文件并保存 六、在 Hexo 文件夹下分别执行以下命令12$ hexo g$ hexo d 或者直接执行1$ hexo g -d 执行完之后会让你输入你的 Github 的账号和密码,如果此时报以下错误,说明你的 deployer 没有安装成功1ERROR Deployer not found: git 需要执行以下命令再安装一次:1npm install hexo-deployer-git --save 再执行 hexo g -d,你的博客就会部署到 Github 上了 七、访问博客 你的博客地址:https://你的用户名.github.io,比如我的是:https://trhx.github.io ,现在每个人都可以通过此链接访问你的博客了 – 如何在博客上发表文章博客已经成功搭建了,但是我们该怎么写博客呢? 一、新建一个空文章,输入以下命令,会在项目 \\Hexo\\source\\_posts 中生成 文章标题.md 文件,文章标题根据需要命名1$ hexo n "文章标题" 也可以直接在 \\Hexo\\source\\_posts 目录下右键鼠标新建文本文档,改后缀为 .md 即可,这种方法比较方便 二、用编辑器编写文章 md 全称 Markdown, Markdown 是 2004 年由 John Gruberis 设计和开发的纯文本格式的语法,非常的简单实用,常用的标记符号屈指可数,几分钟即可学会, .md 文件可以使用支持 Markdown 语法的编辑器编辑,然后将写好的文章(.md文件)保存到 \\Hexo\\source\\_posts 文件夹下即可推荐 Windows 上使用 MarkdownPad2 或者 小书匠 编辑器,macOS 上使用 Mou 编辑器,Linux 上使用 Remarkable 编辑器,Web 端上使用 简书 ,另外可以参考我的另一篇文章:《主流 Markdown 编辑器推荐》当我们用编辑器写好文章后,可以使用以下命令将其推送到服务器上12$ hexo g$ hexo d或者将两个命令合二为一输入以下命令:1$ hexo d -g现在访问你的博客就可以看见写好的文章啦!参考资料:《10款流行的Markdown编辑器》 (By xiaoxiao_engineer) 《献给写作者的 Markdown 新手指南》 (By 简书) 《认识与入门 Markdown》 (By Te_Lee) 《markdown简明语法》 (By 不如) 《markdown基本语法》 (By 高鸿祥) 《Markdown 公式指导手册》 (By Harries)# – 如何为博客更换自己喜欢的主题 博客也搭建好了,文章也会写了,但是!!!默认的主题并不喜欢怎么办?现在,我们就来为自己的博客更换自己喜欢的主题 点击此处进入 Hexo 官网的主题专栏,我们可以看见有许多的主题供我们选择 我们要做的就是把主题克隆过来,在此我们以主题 Aero-Dual 为例,点进去我们就可以看见该主题作者的博客,鼠标滑到底,我们可以看见 Theme By Levblanc 的字样(其他主题类似),点击作者 Levblanc ,页面就会跳转到该主题所有的相关文件在 Github 上的地址,复制该地址 再打开 Hexo 文件夹下的 themes 目录(如:E:\\TRHX_Blog\\Hexo\\themes),右键 Git Bash Here,输入以下命令:1$ git clone 此处填写你刚才复制的主题地址 比如要安装 Aero-Dual 主题,则输入命令:1$ git clone https://github.com/levblanc/hexo-theme-aero-dual 等待下载完成后即可在 themes 目录下生成 hexo-theme-aero-dual 文件夹,然后打开 Hexo 文件夹下的配置文件 _config.yml ,找到关键字 theme,修改参数为:theme:hexo-theme-aero-dual (其他主题修改成相应名称即可),再次注意冒号后面有一个空格! 返回 Hexo 目录,右键 Git Bash Here ,输入以下命令开始部署主题:12$ hexo g $ hexo s 此时打开浏览器,访问 http://localhost:4000/ 就可看见我们的主题已经更换了,如果感觉效果满意,我们就可以把它部署到Github上了 打开 Hexo 文件夹,右键 Git Bash Here ,输入以下命令:123$ hexo clean //该命令的作用是清除缓存,若不输入此命令,服务器有可能更新不了主题$ hexo g -d 此时访问自己的博客即可看见更换后的主题,但我们仍然需要对主题的相关配置进行修改,比如网站标题,图标等等,Hexo 中有两份主要的配置文件,名称都是 _config.yml ,它们均是用于站点配置使用的。其中,一份位于站点根目录下(比如我的:E:\\TRHX_Blog\\Hexo\\_config.yml),主要包含 Hexo 本身整站的配置;另一份位于主题目录下(比如我的:E:\\TRHX_Blog\\Hexo\\themes\\hexo-theme-aero-dual\\_config.yml),这份配置由主题作者提供,主要用于配置主题相关的选项,一般 _config.yml 文件里都有相关注释,按需修改即可 参考资料:《有哪些好看的 Hexo 主题?》 (知乎) 《Hexo | 配置》 (Hexo官方文档) 《hexo常用命令笔记》 (By 小弟调调) – 为你的 Hexo 博客配置个性域名本人在配置域名的时候问题百出,百度的各种方法都不管用,打开网站总是 404,可能是我太笨了 o(╥﹏╥)o ,不过好在后来终于解决了这个问题 首先我们要购买域名,阿里云,腾讯云都可以,也不贵,一年几十块钱,最便宜几块钱也能买到,以阿里云为例,我购买的域名是 itrhx.com,购买过程就不赘述了,选择阿里云的解析平台,来到阿里云的管理控制台,点击进入域名解析列表或者直接点击域名后面的解析 方法一:点击添加记录,需要添加两个记录,两个记录类型都是 CNAME ,第一个主机记录为 @ ,第二个主机记录为 www,记录值都是填你自己的博客地址(比如我的是:trhx.github.io),保存之后域名解析就完成了!方法二:两个记录类型为 A ,第一个主机记录为 @ ,第二个主机记录为 www,记录值都为博客的 IP 地址,IP 地址可以 cmd 中输入 ping 你的博客地址 获得(比如我的:ping trhx.github.io),保存之后域名解析就完成了!有关解析记录类型的区别可以参考《域名解析中A记录、CNAME、MX记录、NS记录的区别和联系》 为了使 GitHub 接收我们的域名,还需要在博客的根目录下添加一个名为 CNAME 的文件(注意不要加.txt,没有任何后缀名!),这个文件放到 Hexo 文件夹的 source 里面,(比如我的是:E:\\TRHX_Blog\\Hexo\\source),文件里面填写你的域名(加不加www都行),比如要填写我的域名,文件里面就写:www.itrhx.com 或者 itrhx.com,经过以上操作,别人就可以通过 www.itrhx.com 、itrhx.com 、trhx.github.io 三个当中任意一个访问我的博客了!你的也一样! 有关加不加www的问题有以下区别: 如果你填写的是没有www的,比如 itrhx.com,那么无论是访问 https://www.itrhx.com 还是 https://itrhx.com ,都会自动跳转到 https://itrhx.com 如果你填写的是带www的,比如 www.itrhx.com ,那么无论是访问 https://www.itrhx.com 还是 https://itrhx.com ,都会自动跳转到 http://www.itrhx.com 如果你在其他平台购买域名,或者选择 DNSPod 等其他域名解析,操作方法大同小异,遇到问题可自行百度解决! 参考资料:《推荐几家域名注册服务商》 (By Jelly Bool) 《盘点十大免费DNS域名解析服务:稳定、可靠》 – 结语一顿操作下来虽然有点儿累,但看见拥有了自己的博客还是非常有成就感的,人生就是需要折腾,那么现在就开始你的创作之旅吧!文章的不断积累,你会从中受益很多的!另外,这是一篇小白写的适用于小白的博客搭建教程,比较详细,有这方面基础的可以百度有简略一点儿的教程,文中如有错误还请大佬指出改正!文中涉及参考资料如有侵权请联系我删除!","categories":[{"name":"Hexo","slug":"Hexo","permalink":"https://www.itrhx.com/categories/Hexo/"}],"tags":[{"name":"Github Pages","slug":"Github-Pages","permalink":"https://www.itrhx.com/tags/Github-Pages/"},{"name":"Hexo","slug":"Hexo","permalink":"https://www.itrhx.com/tags/Hexo/"}]},{"title":"Hello World!","slug":"A01-hello-world","date":"2018-08-10T09:38:00.000Z","updated":"2019-09-09T14:13:26.239Z","comments":true,"path":"2018/08/10/A01-hello-world/","link":"","permalink":"https://www.itrhx.com/2018/08/10/A01-hello-world/","excerpt":"","text":"人类的幸福和欢乐在于奋斗,而最有价值的是为理想而奋斗! ——— 苏格拉底 Human happiness and joy lie in struggle, and what is most valuable is striving for ideals! ——— Socrates","categories":[{"name":"BLOG","slug":"BLOG","permalink":"https://www.itrhx.com/categories/BLOG/"}],"tags":[{"name":"BLOG","slug":"BLOG","permalink":"https://www.itrhx.com/tags/BLOG/"}]}]} \ No newline at end of file diff --git a/friends/index.html b/friends/index.html index 5e7a5011df7867af7eb094e3433e125f0505a8b7..46ddad87cff07636a08f1b655c5ab1eda61e218c 100644 --- a/friends/index.html +++ b/friends/index.html @@ -110,7 +110,7 @@ - + diff --git a/index.html b/index.html index c0c95e54d6e9a63ae1439c315ab1d21b7234a03f..5991af14d6439cdf554ab2997d64662cfb126b94 100644 --- a/index.html +++ b/index.html @@ -110,7 +110,7 @@ - + diff --git a/page/2/index.html b/page/2/index.html index bc63b006daa444d050943d99dc7f6c9bba1eb905..44eb0fb8d2891c4568bc0cc510c6bdeaca51e250 100644 --- a/page/2/index.html +++ b/page/2/index.html @@ -110,7 +110,7 @@ - + diff --git a/page/3/index.html b/page/3/index.html index 4a85ccd42a40361f017835892624acd38559fdd6..6655f41aa07a0eeb719963c250e5be00695a3925 100644 --- a/page/3/index.html +++ b/page/3/index.html @@ -110,7 +110,7 @@ - + diff --git a/page/4/index.html b/page/4/index.html index 2e67dc6d9f7d637b6598c2bc040a4ec714050761..25477b9560f359244599a1a1d52cd856c5605faa 100644 --- a/page/4/index.html +++ b/page/4/index.html @@ -110,7 +110,7 @@ - + diff --git a/page/5/index.html b/page/5/index.html index 03b011bfb3bef4d8cd73ae281851f30cd6bff4f4..cdd973f4aa4ca0525992e58047b35a4292e47592 100644 --- a/page/5/index.html +++ b/page/5/index.html @@ -110,7 +110,7 @@ - + diff --git a/page/6/index.html b/page/6/index.html index fe233cf51aafecd396a380216d5050c5b027e2bf..d26669c109cd33f13257e6abcf38117506b6615e 100644 --- a/page/6/index.html +++ b/page/6/index.html @@ -110,7 +110,7 @@ - + diff --git a/page/7/index.html b/page/7/index.html index 31ed0e1a55dc315f08c1b5ec9f747b376d69fa64..f09d57f3d3a391d28fb5c34f8920379590410a2e 100644 --- a/page/7/index.html +++ b/page/7/index.html @@ -110,7 +110,7 @@ - + diff --git a/page/8/index.html b/page/8/index.html index 0845b7593a160e1e59e232056ebdf2a9395d79c1..1e84649b1c5a9bbcd7d7b253b42f87d3020dad9c 100644 --- a/page/8/index.html +++ b/page/8/index.html @@ -110,7 +110,7 @@ - + @@ -582,10 +582,10 @@ diff --git a/page/9/index.html b/page/9/index.html index f89681e6aca2b50b7510698090670262dbcd3bf0..063c14cbdaa77ec0a5cd7d322a32bda640eda76e 100644 --- a/page/9/index.html +++ b/page/9/index.html @@ -110,7 +110,7 @@ - + @@ -1229,10 +1229,10 @@
-  主题个性化 -  Hexo +  主题个性化 +  Material X  spfk @@ -1508,10 +1508,10 @@ diff --git a/search.xml b/search.xml index 3a75fd3763e99dfb179f14a8a72063cfd78ea82f..2ae8924cfeb125fa36ed21085993a8a35fa8f0ae 100644 --- a/search.xml +++ b/search.xml @@ -956,8 +956,8 @@ 学习经验 - Pygame Python + Pygame @@ -1140,8 +1140,8 @@ Hexo - 主题个性化 Hexo + 主题个性化 Material X spfk @@ -1166,8 +1166,8 @@ Hexo - Hexo Github Pages + Hexo diff --git a/sitemap.xml b/sitemap.xml index 12050105c837faa7a83b4abceba7665633dd9aa3..0d7b3aa5c1f6c49f9ee8622c548dd43c6bf1aa53 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -422,28 +422,28 @@ - https://www.itrhx.com/games/cat/index.html + https://www.itrhx.com/box/about/index.html 2019-12-29T06:55:50.751Z - https://www.itrhx.com/games/2048/index.html + https://www.itrhx.com/games/element/index.html 2019-12-29T06:55:50.751Z - https://www.itrhx.com/games/element/index.html + https://www.itrhx.com/games/cat/index.html 2019-12-29T06:55:50.751Z - https://www.itrhx.com/box/about/index.html + https://www.itrhx.com/games/2048/index.html 2019-12-29T06:55:50.751Z diff --git a/tags/12306/index.html b/tags/12306/index.html index 39305e7419b31230cb3c5e3b4b1afd30c4c090dd..430f9a6b2583d998d7cd2014b33ef5903423014d 100644 --- a/tags/12306/index.html +++ b/tags/12306/index.html @@ -110,7 +110,7 @@ - + diff --git "a/tags/3D\345\233\276/index.html" "b/tags/3D\345\233\276/index.html" index 1f86fea46a701282beee2434a5ce9474aa6b0c51..a032121039d83afbe6a39d43624c4207f806b2bf 100644 --- "a/tags/3D\345\233\276/index.html" +++ "b/tags/3D\345\233\276/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/58\345\220\214\345\237\216/index.html" "b/tags/58\345\220\214\345\237\216/index.html" index 2cb6a0c1bbc8efc1b0fbbb53e206bad1cf1730e2..cb630d2edc597a3565c4e1bfad15a0efeeaef2ac 100644 --- "a/tags/58\345\220\214\345\237\216/index.html" +++ "b/tags/58\345\220\214\345\237\216/index.html" @@ -110,7 +110,7 @@ - + diff --git a/tags/Ajax/index.html b/tags/Ajax/index.html index 46e71e2af47ea47490e63595d1e7caa55dd9caa3..5feb57814d3d06dd6b4e438d481f624384e04c3a 100644 --- a/tags/Ajax/index.html +++ b/tags/Ajax/index.html @@ -110,7 +110,7 @@ - + diff --git a/tags/BLOG/index.html b/tags/BLOG/index.html index 0d6c22699e83bef62c925c18322fdf01bb1bce29..2c38ad70178391ab4818db5ef4d74184dbadd3c9 100644 --- a/tags/BLOG/index.html +++ b/tags/BLOG/index.html @@ -110,7 +110,7 @@ - + diff --git a/tags/Beautiful-Soup/index.html b/tags/Beautiful-Soup/index.html index 39e4e57dafb9383d33936eed058c0ffa11cb5ef4..9989811db2c9b59c891906488382746089e76682 100644 --- a/tags/Beautiful-Soup/index.html +++ b/tags/Beautiful-Soup/index.html @@ -110,7 +110,7 @@ - + diff --git a/tags/CDN/index.html b/tags/CDN/index.html index 631192e5472e881ad87480b1486dd8656d87d884..82c74c636c156271c1b8ad624c809a3c0d3f6839 100644 --- a/tags/CDN/index.html +++ b/tags/CDN/index.html @@ -110,7 +110,7 @@ - + diff --git a/tags/Coding-Pages/index.html b/tags/Coding-Pages/index.html index b12d421f5e3ad5d95bc636df366953ca248d332b..41e27ca4fc82e145ace2c668b7d28f8895ad5da5 100644 --- a/tags/Coding-Pages/index.html +++ b/tags/Coding-Pages/index.html @@ -110,7 +110,7 @@ - + diff --git a/tags/DataFrame/index.html b/tags/DataFrame/index.html index 237577037b6a40d021fa0909d56f185734e3ad15..4d7d25047f9487bf19befc770a11ed62c1a20c6a 100644 --- a/tags/DataFrame/index.html +++ b/tags/DataFrame/index.html @@ -110,7 +110,7 @@ - + diff --git a/tags/Deepin/index.html b/tags/Deepin/index.html index f0c82d54c6677c9620404bbfa6b7dce405388c08..a0005fec3e6b656bb4a446dddd6dd8214c0dac74 100644 --- a/tags/Deepin/index.html +++ b/tags/Deepin/index.html @@ -110,7 +110,7 @@ - + diff --git a/tags/Elicpse/index.html b/tags/Elicpse/index.html index fc4bac1805f1f9f99385cd47014aafb49c562077..7b08ef67f0b5c1a463af20dc97e3bf2837f73274 100644 --- a/tags/Elicpse/index.html +++ b/tags/Elicpse/index.html @@ -110,7 +110,7 @@ - + diff --git a/tags/Github-Pages/index.html b/tags/Github-Pages/index.html index cb9e2e5252d23d9ce56d868d8d3823f453183743..6375470c490b1cce7f00e94811a3ccf94844d2b8 100644 --- a/tags/Github-Pages/index.html +++ b/tags/Github-Pages/index.html @@ -110,7 +110,7 @@ - + diff --git a/tags/Github/index.html b/tags/Github/index.html index a7eee69518e1c4d65dc8e4fc2b505925c23597ad..aa317d4dbc0a5b149b36d16dbc8ee3397cad6d46 100644 --- a/tags/Github/index.html +++ b/tags/Github/index.html @@ -110,7 +110,7 @@ - + diff --git a/tags/GroupBy/index.html b/tags/GroupBy/index.html index c91e36bf1e79f0fa7cc9ca901c6cd3f4993e322d..921f217f41f1bd9ac204ff43a4e4f6fbcfb8ba0e 100644 --- a/tags/GroupBy/index.html +++ b/tags/GroupBy/index.html @@ -110,7 +110,7 @@ - + diff --git a/tags/HTTPS/index.html b/tags/HTTPS/index.html index 9e09d6084198334ffa43800576e168c247ac2bae..ba78abfc30d3a8cd0fd071e55a8d3c7fdd98941b 100644 --- a/tags/HTTPS/index.html +++ b/tags/HTTPS/index.html @@ -110,7 +110,7 @@ - + diff --git a/tags/Hexo/index.html b/tags/Hexo/index.html index 9ee101fe8fb47b595569dd25643241224a8fa2f2..674bcdb250f8542026c24482d14dad7133dc7576 100644 --- a/tags/Hexo/index.html +++ b/tags/Hexo/index.html @@ -110,7 +110,7 @@ - + @@ -1342,10 +1342,10 @@
-  主题个性化 -  Hexo +  主题个性化 +  Material X  spfk @@ -1492,10 +1492,10 @@ diff --git "a/tags/ICP\345\244\207\346\241\210/index.html" "b/tags/ICP\345\244\207\346\241\210/index.html" index 5d19dc1a58b80ced10761e229a9584ff0698455e..aa0541573002adb8d59b6866caa5cbc84d0a1821 100644 --- "a/tags/ICP\345\244\207\346\241\210/index.html" +++ "b/tags/ICP\345\244\207\346\241\210/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/IO\346\223\215\344\275\234/index.html" "b/tags/IO\346\223\215\344\275\234/index.html" index 9d28edd9136e1c62b511ac7eeaf0ada39731c57e..8fffc0b7291714a5af8adbe0ab4358d58d7dc6ce 100644 --- "a/tags/IO\346\223\215\344\275\234/index.html" +++ "b/tags/IO\346\223\215\344\275\234/index.html" @@ -110,7 +110,7 @@ - + diff --git a/tags/Index/index.html b/tags/Index/index.html index e1f2f95477e8cca339a8d48a5a2674ad8080996c..2d91328f28652c5d694c0e86159061e89450118c 100644 --- a/tags/Index/index.html +++ b/tags/Index/index.html @@ -110,7 +110,7 @@ - + diff --git a/tags/JDBC/index.html b/tags/JDBC/index.html index 33f175ded19ca3fbbc250c7918c65f0a69f870b3..4bc359d17b085b8eaf8323741babc905db7e4bd5 100644 --- a/tags/JDBC/index.html +++ b/tags/JDBC/index.html @@ -110,7 +110,7 @@ - + diff --git "a/tags/JS-\351\242\204\345\212\240\350\275\275/index.html" "b/tags/JS-\351\242\204\345\212\240\350\275\275/index.html" index 0be4d37defdb64a0b29bc502e780bf609b5c92e3..e68232f724b9f66fcffac8709d1c922219fdcb33 100644 --- "a/tags/JS-\351\242\204\345\212\240\350\275\275/index.html" +++ "b/tags/JS-\351\242\204\345\212\240\350\275\275/index.html" @@ -110,7 +110,7 @@ - + diff --git a/tags/LaTeX/index.html b/tags/LaTeX/index.html index 94986f71469794ffa80a3b4e0d9b387eef7b6a61..b7917222db5c10e6c29df68886b571f63b0dfa8d 100644 --- a/tags/LaTeX/index.html +++ b/tags/LaTeX/index.html @@ -110,7 +110,7 @@ - + diff --git a/tags/Markdown/index.html b/tags/Markdown/index.html index 9b65454f96e3b92146a8fa3b00dd9f977f6cbceb..0533ba06a5b48b734f22ada3bad13e9669495021 100644 --- a/tags/Markdown/index.html +++ b/tags/Markdown/index.html @@ -110,7 +110,7 @@ - + diff --git a/tags/Material-X/index.html b/tags/Material-X/index.html index 0d9f0a6929d82a36583ac00ca8da063205050ea2..23ba5b018d39b81ec4ed3460fd9a99db9fad6beb 100644 --- a/tags/Material-X/index.html +++ b/tags/Material-X/index.html @@ -110,7 +110,7 @@ - + @@ -547,10 +547,10 @@
-  主题个性化 -  Hexo +  主题个性化 +  Material X  spfk diff --git a/tags/Matplotlib/index.html b/tags/Matplotlib/index.html index dc91f77a548795a0d47514b00d733c8ec0f7acf7..aea6032ff62187c0baa8396aa1d102c091f4d3c8 100644 --- a/tags/Matplotlib/index.html +++ b/tags/Matplotlib/index.html @@ -110,7 +110,7 @@ - + diff --git a/tags/Matplotlib/page/2/index.html b/tags/Matplotlib/page/2/index.html index 6c9d62f1958d82aac187d1d9e6c4373590895c65..02302f0a02ea35b67e0988ad3ea034634e8a1f79 100644 --- a/tags/Matplotlib/page/2/index.html +++ b/tags/Matplotlib/page/2/index.html @@ -110,7 +110,7 @@ - + diff --git a/tags/MongoDB/index.html b/tags/MongoDB/index.html index e9057db32c7b039a736218d4a8f6e592c86bf7b1..3e3f187e9bb42a0cd6d9d9b1f600d7c0c24836cc 100644 --- a/tags/MongoDB/index.html +++ b/tags/MongoDB/index.html @@ -110,7 +110,7 @@ - + diff --git a/tags/MySQL/index.html b/tags/MySQL/index.html index ff5c61dc0243cf95500371d3778adb50d3e386a5..75ed9e54da373e4673517b17466c5f11c404677c 100644 --- a/tags/MySQL/index.html +++ b/tags/MySQL/index.html @@ -110,7 +110,7 @@ - + diff --git a/tags/NumPy/index.html b/tags/NumPy/index.html index 08995198f4babbb6756073e1862e78e3d352f883..baef91ed9d60759c4f9060de6c219e84a11e4b86 100644 --- a/tags/NumPy/index.html +++ b/tags/NumPy/index.html @@ -110,7 +110,7 @@ - + diff --git a/tags/PEP8/index.html b/tags/PEP8/index.html index c687873383cc2bef0c2f35ddc2041a9e176c70b6..d5ee9ec33ceac89bd7574f0458679da54e2492af 100644 --- a/tags/PEP8/index.html +++ b/tags/PEP8/index.html @@ -110,7 +110,7 @@ - + diff --git a/tags/Pandas/index.html b/tags/Pandas/index.html index 06fe291e5e13f13fa7f50bb5380f5dc7f45f6f26..88398ff9ab72d9500537d0daac32e29152c21022 100644 --- a/tags/Pandas/index.html +++ b/tags/Pandas/index.html @@ -110,7 +110,7 @@ - + diff --git a/tags/PicGo/index.html b/tags/PicGo/index.html index 5ea8601380595eb767c5c36ac1b36bb44141b26d..ba97824e794b8919a7988b472ae6cdad8fec3811 100644 --- a/tags/PicGo/index.html +++ b/tags/PicGo/index.html @@ -110,7 +110,7 @@ - + diff --git a/tags/Pygame/index.html b/tags/Pygame/index.html index 551b668aca2d17e58eafdc836e291ad540bae156..9081397ec21b55015b399feead20c4f98bcbd8d6 100644 --- a/tags/Pygame/index.html +++ b/tags/Pygame/index.html @@ -110,7 +110,7 @@ - + @@ -547,10 +547,10 @@ diff --git a/tags/Python/index.html b/tags/Python/index.html index 4a40ba186c2851a6429fd57b931b7dfef58251d5..f7bbcd5b83d639e4010a790363b5bbee848ad960 100644 --- a/tags/Python/index.html +++ b/tags/Python/index.html @@ -110,7 +110,7 @@ - + @@ -678,10 +678,10 @@ diff --git a/tags/Redis/index.html b/tags/Redis/index.html index d596e1abef2fe24258b5972fb82b098f358498a4..30fe89d68fb83a776f4a7793050a63746104bb73 100644 --- a/tags/Redis/index.html +++ b/tags/Redis/index.html @@ -110,7 +110,7 @@ - + diff --git a/tags/SEO/index.html b/tags/SEO/index.html index d8af95276bd03dad90c1c9278aee1032ad408376..71ba759fb07392d697f95dfdd872493f1334f6eb 100644 --- a/tags/SEO/index.html +++ b/tags/SEO/index.html @@ -110,7 +110,7 @@ - + diff --git a/tags/SQL-Server-2012/index.html b/tags/SQL-Server-2012/index.html index 40ab7bc8aaf626dfff878b4429e47d9a5e4146dc..60f166e5ef94472ac14ec3e521bb6180b4abd45b 100644 --- a/tags/SQL-Server-2012/index.html +++ b/tags/SQL-Server-2012/index.html @@ -110,7 +110,7 @@ - + diff --git a/tags/SSR/index.html b/tags/SSR/index.html index 09df72f3f1bb6d76fe645f7854458fb2127fbb31..20553940b8fbd39d1f289bc54b7fecb505461646 100644 --- a/tags/SSR/index.html +++ b/tags/SSR/index.html @@ -110,7 +110,7 @@ - + diff --git a/tags/Selenium/index.html b/tags/Selenium/index.html index f99fa57da0c0f10eedde2d4d26a2fe496680a7d5..93b48abe1aa72181db21c33d80f79b8dabdb345a 100644 --- a/tags/Selenium/index.html +++ b/tags/Selenium/index.html @@ -110,7 +110,7 @@ - + diff --git a/tags/Series/index.html b/tags/Series/index.html index 0973d8744e9042a01a5cbc0fd391dd8cb9e10f21..02376fb359c7347deb82234c217dd2088dd3734c 100644 --- a/tags/Series/index.html +++ b/tags/Series/index.html @@ -110,7 +110,7 @@ - + diff --git a/tags/Ubuntu/index.html b/tags/Ubuntu/index.html index a199abbc43376fba9b860a78731359ce1997591e..04043155e0945e0a7fa93b752ae63d4131a9eff6 100644 --- a/tags/Ubuntu/index.html +++ b/tags/Ubuntu/index.html @@ -110,7 +110,7 @@ - + diff --git a/tags/User-Agent/index.html b/tags/User-Agent/index.html index f289aa3dffd86def36b06d33990775206456cdc7..e9d94c1b4a64313058fcda7cbb17d03fc0b55111 100644 --- a/tags/User-Agent/index.html +++ b/tags/User-Agent/index.html @@ -110,7 +110,7 @@ - + diff --git a/tags/VMware/index.html b/tags/VMware/index.html index 2e90454b823b255889e1a42ee766ebb146409f2e..241a7c9b1fa2d204382b17a387fe72847846bdf8 100644 --- a/tags/VMware/index.html +++ b/tags/VMware/index.html @@ -110,7 +110,7 @@ - + diff --git a/tags/VPS/index.html b/tags/VPS/index.html index 4fa2c33f1b6b5a9b49c5c7f3f9a129400a7a24b2..6d784c16ee2ad3ca748ba137050c18a56860ba7e 100644 --- a/tags/VPS/index.html +++ b/tags/VPS/index.html @@ -110,7 +110,7 @@ - + diff --git a/tags/XPath/index.html b/tags/XPath/index.html index 8c543077e5622c10651c20fbe4a546db6660dadd..3bc47fb046fbdcc0c06c37bcd16b67db54091712 100644 --- a/tags/XPath/index.html +++ b/tags/XPath/index.html @@ -110,7 +110,7 @@ - + diff --git "a/tags/if\350\257\255\345\217\245/index.html" "b/tags/if\350\257\255\345\217\245/index.html" index fb06e1e7220348489d0c551f6ca868ba1339147e..86437f661f84e235a3f26bd3300cb0aa28b9683c 100644 --- "a/tags/if\350\257\255\345\217\245/index.html" +++ "b/tags/if\350\257\255\345\217\245/index.html" @@ -110,7 +110,7 @@ - + diff --git a/tags/index.html b/tags/index.html index ed0d24d6a12ce7364d6a4924efceb49d489eacdf..f7c4656823cb2a6c56dc980adf06a1fff7151a43 100644 --- a/tags/index.html +++ b/tags/index.html @@ -110,7 +110,7 @@ - + diff --git "a/tags/input-\345\207\275\346\225\260/index.html" "b/tags/input-\345\207\275\346\225\260/index.html" index f680974ed4417a1083d6d754eb63e021daf17697..f6439d126d3afb79887256205b801bce3ff7a3f9 100644 --- "a/tags/input-\345\207\275\346\225\260/index.html" +++ "b/tags/input-\345\207\275\346\225\260/index.html" @@ -110,7 +110,7 @@ - + diff --git a/tags/instant-page/index.html b/tags/instant-page/index.html index d52055e8d8b57faff8ab187c375fdbbdec9529a4..b48d9bfb8ff2b1fc2cb5717690e083ba373303db 100644 --- a/tags/instant-page/index.html +++ b/tags/instant-page/index.html @@ -110,7 +110,7 @@ - + diff --git a/tags/jsDelivr/index.html b/tags/jsDelivr/index.html index 22e59bb57c3c33b1191b3fb80d85837c174ca913..954131d3b441385e32c1eed7982c1d76d530e024 100644 --- a/tags/jsDelivr/index.html +++ b/tags/jsDelivr/index.html @@ -110,7 +110,7 @@ - + diff --git a/tags/lxml/index.html b/tags/lxml/index.html index c3eea560fe0d24d56fbac36b82a8d7704f72598d..d8d63bf79f6b17eb323a1d9a945b26e2595cdbd9 100644 --- a/tags/lxml/index.html +++ b/tags/lxml/index.html @@ -110,7 +110,7 @@ - + diff --git a/tags/matplotibrc/index.html b/tags/matplotibrc/index.html index 5cbbaeb3cf3a9f31d725de2a6abaa29fcb664738..f846d1f8d28a1a719ae4ba39c029db263f53bbe6 100644 --- a/tags/matplotibrc/index.html +++ b/tags/matplotibrc/index.html @@ -110,7 +110,7 @@ - + diff --git a/tags/pyspider/index.html b/tags/pyspider/index.html index 59f47643c1755dee6651ac9f10e3a1024ebbe979..f413f7d026fff1dc7e6464c25b470b66a2dbb8f4 100644 --- a/tags/pyspider/index.html +++ b/tags/pyspider/index.html @@ -110,7 +110,7 @@ - + diff --git a/tags/requests/index.html b/tags/requests/index.html index 5d2fc6e716df0a4e4bacb07ec3af7918fc8ffb61..71a4c21239eed988e6cefdac6555286080a50ac7 100644 --- a/tags/requests/index.html +++ b/tags/requests/index.html @@ -110,7 +110,7 @@ - + diff --git a/tags/spfk/index.html b/tags/spfk/index.html index ceaeda7dd19f1e4c3e3cd1395c0ef77e8a82092e..54a54bd4c50324b938e51ccd22f24a5da5c9d6e5 100644 --- a/tags/spfk/index.html +++ b/tags/spfk/index.html @@ -110,7 +110,7 @@ - + @@ -547,10 +547,10 @@
-  主题个性化 -  Hexo +  主题个性化 +  Material X  spfk diff --git a/tags/urllib/index.html b/tags/urllib/index.html index 393c1e7187832d07d91a208aba8e02951438db15..ae25e9f87088d58f6dcdddb085aa674d9738090d 100644 --- a/tags/urllib/index.html +++ b/tags/urllib/index.html @@ -110,7 +110,7 @@ - + diff --git "a/tags/while\345\276\252\347\216\257/index.html" "b/tags/while\345\276\252\347\216\257/index.html" index ef38ebfbc87bcf8d8cc869fabc1efd7cf18800db..a9d3e160ad061329d00f387feccfb2eb7b92119b 100644 --- "a/tags/while\345\276\252\347\216\257/index.html" +++ "b/tags/while\345\276\252\347\216\257/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\344\270\273\351\242\230\344\270\252\346\200\247\345\214\226/index.html" "b/tags/\344\270\273\351\242\230\344\270\252\346\200\247\345\214\226/index.html" index 02b25ae30f6db3e522cf26dec31edec19f3aee0e..a2ab5be6697a9a104c3d88c0700a90d2a2350e63 100644 --- "a/tags/\344\270\273\351\242\230\344\270\252\346\200\247\345\214\226/index.html" +++ "b/tags/\344\270\273\351\242\230\344\270\252\346\200\247\345\214\226/index.html" @@ -110,7 +110,7 @@ - + @@ -547,10 +547,10 @@
-  主题个性化 -  Hexo +  主题个性化 +  Material X  spfk diff --git "a/tags/\344\273\243\347\220\206/index.html" "b/tags/\344\273\243\347\220\206/index.html" index 4e282d6a1cd212b84b199e51aa415d00b40560f5..ffe1930dc9ba51e780e0332c1cf1298f196d2f4e 100644 --- "a/tags/\344\273\243\347\220\206/index.html" +++ "b/tags/\344\273\243\347\220\206/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\344\275\215\350\277\220\347\256\227/index.html" "b/tags/\344\275\215\350\277\220\347\256\227/index.html" index 88bccf6cb01ca5c0727d16a003aa762a19a8d914..73c2b19a980928c423208a5258a1ceb1e445282d 100644 --- "a/tags/\344\275\215\350\277\220\347\256\227/index.html" +++ "b/tags/\344\275\215\350\277\220\347\256\227/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\345\205\254\345\256\211\345\244\207\346\241\210/index.html" "b/tags/\345\205\254\345\256\211\345\244\207\346\241\210/index.html" index 595a8b5d747655f13af9796ffd5d792feee8a02d..fd23eb86f86587a9866c6da2c5fa553a2c5c855a 100644 --- "a/tags/\345\205\254\345\256\211\345\244\207\346\241\210/index.html" +++ "b/tags/\345\205\254\345\256\211\345\244\207\346\241\210/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\345\207\275\346\225\260/index.html" "b/tags/\345\207\275\346\225\260/index.html" index e8f857e1ca2a7385f95837ed2d103f5fbe6e8078..99aae1c35cb7fcc4ee2a6530417a50658857b45a 100644 --- "a/tags/\345\207\275\346\225\260/index.html" +++ "b/tags/\345\207\275\346\225\260/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\345\207\275\346\225\260\345\272\224\347\224\250/index.html" "b/tags/\345\207\275\346\225\260\345\272\224\347\224\250/index.html" index f67b02546d013c7864892773f6c45c5dc93fe818..1fd8d697a2fd58c5d597eb0e25a10f057a8e1e24 100644 --- "a/tags/\345\207\275\346\225\260\345\272\224\347\224\250/index.html" +++ "b/tags/\345\207\275\346\225\260\345\272\224\347\224\250/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\345\210\206\345\211\262/index.html" "b/tags/\345\210\206\345\211\262/index.html" index 28ccddb35392518b2de85a05df8ccc1942e5e3c6..6b49ee2a74b5ec15916ac7b4a36dd1067f294a56 100644 --- "a/tags/\345\210\206\345\211\262/index.html" +++ "b/tags/\345\210\206\345\211\262/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\345\210\207\347\211\207/index.html" "b/tags/\345\210\207\347\211\207/index.html" index 1076f721d165c159cd29f6a0a775b827b9349c8a..545bc6b4c5705382064082598acb3c9695c9fae8 100644 --- "a/tags/\345\210\207\347\211\207/index.html" +++ "b/tags/\345\210\207\347\211\207/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\345\210\227\350\241\250/index.html" "b/tags/\345\210\227\350\241\250/index.html" index 14273cda769306410e30b09791557a8e2ed12c46..4b9bd67c7d81a885be59d346a9f4ba3ee612fb2e 100644 --- "a/tags/\345\210\227\350\241\250/index.html" +++ "b/tags/\345\210\227\350\241\250/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\345\210\244\346\226\255\345\207\275\346\225\260/index.html" "b/tags/\345\210\244\346\226\255\345\207\275\346\225\260/index.html" index 2fb22041e8c8ebe6abd64463b23e85b92bc114f3..96a9c6ea14d3e302182761e2ad5f61859664a4c1 100644 --- "a/tags/\345\210\244\346\226\255\345\207\275\346\225\260/index.html" +++ "b/tags/\345\210\244\346\226\255\345\207\275\346\225\260/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\345\211\215\347\250\213\346\227\240\345\277\247/index.html" "b/tags/\345\211\215\347\250\213\346\227\240\345\277\247/index.html" index d6ad8bb41ffc6bc4878f386220c71cc8d06c477e..0c739aa7c6000247692894a957d1311216266d54 100644 --- "a/tags/\345\211\215\347\250\213\346\227\240\345\277\247/index.html" +++ "b/tags/\345\211\215\347\250\213\346\227\240\345\277\247/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\345\217\230\351\207\217/index.html" "b/tags/\345\217\230\351\207\217/index.html" index b465d6ced6fa8e26b4fe6c5e6399d73ab37d04f9..3e4f004ae4911626843dccfa49762633072daa56 100644 --- "a/tags/\345\217\230\351\207\217/index.html" +++ "b/tags/\345\217\230\351\207\217/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\345\220\210\345\271\266\346\225\260\346\215\256\351\233\206/index.html" "b/tags/\345\220\210\345\271\266\346\225\260\346\215\256\351\233\206/index.html" index 11ffe85868bfaf70494ba8b844c63d85f078f40f..3b190c0257497655658f4157d1f9023c73e0c3e6 100644 --- "a/tags/\345\220\210\345\271\266\346\225\260\346\215\256\351\233\206/index.html" +++ "b/tags/\345\220\210\345\271\266\346\225\260\346\215\256\351\233\206/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\345\223\224\345\223\251\345\223\224\345\223\251/index.html" "b/tags/\345\223\224\345\223\251\345\223\224\345\223\251/index.html" index 34ac8b95209e4ba9559da50603fbf9013b9f5d44..27522f2cd865773db7856a225d028995cfc64874 100644 --- "a/tags/\345\223\224\345\223\251\345\223\224\345\223\251/index.html" +++ "b/tags/\345\223\224\345\223\251\345\223\224\345\223\251/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\345\233\276\344\276\213/index.html" "b/tags/\345\233\276\344\276\213/index.html" index 567f1c595107880b3ee9a610471f8ceb74198f30..0aaf085629417aa86431f0cf497f6e26871350f9 100644 --- "a/tags/\345\233\276\344\276\213/index.html" +++ "b/tags/\345\233\276\344\276\213/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\345\233\276\345\272\212/index.html" "b/tags/\345\233\276\345\272\212/index.html" index c6bd9e4b5b6fe8b514e41c48f811a45cc9783e69..11844fda1a145d45f4a5483b25110a81b4ced5f0 100644 --- "a/tags/\345\233\276\345\272\212/index.html" +++ "b/tags/\345\233\276\345\272\212/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\345\233\276\345\275\242\351\252\214\350\257\201\347\240\201/index.html" "b/tags/\345\233\276\345\275\242\351\252\214\350\257\201\347\240\201/index.html" index 3141c1a5ed34e65431244414f560f9b0e15bbac9..19a159f98cde913f1b27885260ad6bc6d28c1b7d 100644 --- "a/tags/\345\233\276\345\275\242\351\252\214\350\257\201\347\240\201/index.html" +++ "b/tags/\345\233\276\345\275\242\351\252\214\350\257\201\347\240\201/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\345\236\203\345\234\276/index.html" "b/tags/\345\236\203\345\234\276/index.html" index 4cd8fd879ba4edb2e3589faf5d95644405d429b7..2758568ea94c3bb5e4b5b67edce869245863b95b 100644 --- "a/tags/\345\236\203\345\234\276/index.html" +++ "b/tags/\345\236\203\345\234\276/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\345\244\207\344\273\275/index.html" "b/tags/\345\244\207\344\273\275/index.html" index 993b03a05acf10e5ff5d736a48dfb2dc93187b43..04fe63ec349fe761a2ed8b76d758d2132fbc1f73 100644 --- "a/tags/\345\244\207\344\273\275/index.html" +++ "b/tags/\345\244\207\344\273\275/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\345\255\220\345\233\276/index.html" "b/tags/\345\255\220\345\233\276/index.html" index 7f1ab731f44665a88ff81e4e8287d2cb82e5389e..b676f501514ee2d068e62f68a1b504ac991cedc9 100644 --- "a/tags/\345\255\220\345\233\276/index.html" +++ "b/tags/\345\255\220\345\233\276/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\345\255\227\345\205\270/index.html" "b/tags/\345\255\227\345\205\270/index.html" index 64cba6662b2a6951eb37e83cc2436bacc0480542..91d813b601d35e617bc0687db47db296c9846d0d 100644 --- "a/tags/\345\255\227\345\205\270/index.html" +++ "b/tags/\345\255\227\345\205\270/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\345\255\227\347\254\246\344\270\262\345\207\275\346\225\260/index.html" "b/tags/\345\255\227\347\254\246\344\270\262\345\207\275\346\225\260/index.html" index bacf938828bc181a167eeb78ce7a0ff493c2855b..19174074c105892618a8f4a9139ece404cbbe453 100644 --- "a/tags/\345\255\227\347\254\246\344\270\262\345\207\275\346\225\260/index.html" +++ "b/tags/\345\255\227\347\254\246\344\270\262\345\207\275\346\225\260/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\345\256\211\345\261\205\345\256\242/index.html" "b/tags/\345\256\211\345\261\205\345\256\242/index.html" index 0ac9bd8eade3ef114c5f9fef9e361e3db588327b..1a93ab844d29bc57ab584d728f27c215f987daef 100644 --- "a/tags/\345\256\211\345\261\205\345\256\242/index.html" +++ "b/tags/\345\256\211\345\261\205\345\256\242/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\345\261\202\347\272\247\347\264\242\345\274\225/index.html" "b/tags/\345\261\202\347\272\247\347\264\242\345\274\225/index.html" index bc4bf78882f5ead041e75cb06d2a19cb20f3e5e2..3efa17d278bed8adc47ddce8f4ba7262d774bf07 100644 --- "a/tags/\345\261\202\347\272\247\347\264\242\345\274\225/index.html" +++ "b/tags/\345\261\202\347\272\247\347\264\242\345\274\225/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\345\271\264\347\273\210\346\200\273\347\273\223/index.html" "b/tags/\345\271\264\347\273\210\346\200\273\347\273\223/index.html" index 30857bedb6de789a6f00c17fac17e2736583d4ba..e97634310b51d01dff684ca6779193a0588f4d31 100644 --- "a/tags/\345\271\264\347\273\210\346\200\273\347\273\223/index.html" +++ "b/tags/\345\271\264\347\273\210\346\200\273\347\273\223/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\345\271\277\346\222\255/index.html" "b/tags/\345\271\277\346\222\255/index.html" index 1d3251643ab1b0f8805160b8426b0b0751c5b20a..2f219b0809acd1199497e98c33a169b61a8697f0 100644 --- "a/tags/\345\271\277\346\222\255/index.html" +++ "b/tags/\345\271\277\346\222\255/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\345\274\202\345\270\270/index.html" "b/tags/\345\274\202\345\270\270/index.html" index 0a47f6b26bededf3cf96b2301348656788628069..2e6da3a1e326eccb11325f1d69e7bde8440c814f 100644 --- "a/tags/\345\274\202\345\270\270/index.html" +++ "b/tags/\345\274\202\345\270\270/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\346\212\200\345\267\247/index.html" "b/tags/\346\212\200\345\267\247/index.html" index 893dfbe96389225cece5d5d989ed469df328d96f..c751cee5eb354f6190eb0ac8de98ee80702a2e62 100644 --- "a/tags/\346\212\200\345\267\247/index.html" +++ "b/tags/\346\212\200\345\267\247/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\346\213\274\346\216\245/index.html" "b/tags/\346\213\274\346\216\245/index.html" index dbcb9936643e5c30a8e3636c5f4a656436eafc8e..e795437e6374b1e49c2caabd5fa2793d0259a62c 100644 --- "a/tags/\346\213\274\346\216\245/index.html" +++ "b/tags/\346\213\274\346\216\245/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\346\216\222\345\272\217/index.html" "b/tags/\346\216\222\345\272\217/index.html" index d5b4a5ff2f7a60c15ce3fe343976387d5398607a..9a51aa06092e479f74d78d4eb66da2461942e32c 100644 --- "a/tags/\346\216\222\345\272\217/index.html" +++ "b/tags/\346\216\222\345\272\217/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\346\216\222\345\272\217\345\207\275\346\225\260/index.html" "b/tags/\346\216\222\345\272\217\345\207\275\346\225\260/index.html" index 243e2fa2404b312185d8fded65243590b4b858bc..dbec135f012099eaff67901821034b06d13803b4 100644 --- "a/tags/\346\216\222\345\272\217\345\207\275\346\225\260/index.html" +++ "b/tags/\346\216\222\345\272\217\345\207\275\346\225\260/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\346\223\215\344\275\234\345\210\227\350\241\250/index.html" "b/tags/\346\223\215\344\275\234\345\210\227\350\241\250/index.html" index 5ac206a630928d36c8ee20f86fcea5b5e725a6ff..864607f9eb0db77c8369d25ef2147b8d5f70b9d5 100644 --- "a/tags/\346\223\215\344\275\234\345\210\227\350\241\250/index.html" +++ "b/tags/\346\223\215\344\275\234\345\210\227\350\241\250/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\346\225\243\347\202\271\345\233\276/index.html" "b/tags/\346\225\243\347\202\271\345\233\276/index.html" index 4259cd4a4e6dc6d0e9b9a3bcd4115918d1608e1f..b7317c9e6a3e1e137d1f4db797ae15acbbbdf051 100644 --- "a/tags/\346\225\243\347\202\271\345\233\276/index.html" +++ "b/tags/\346\225\243\347\202\271\345\233\276/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\346\225\260\345\255\246\345\207\275\346\225\260/index.html" "b/tags/\346\225\260\345\255\246\345\207\275\346\225\260/index.html" index fe0fbaf9fab7f8f3a9ad2109df7c0c197655349d..bf6792da39a5afd3f9869b69a127fc2e9f4cb287 100644 --- "a/tags/\346\225\260\345\255\246\345\207\275\346\225\260/index.html" +++ "b/tags/\346\225\260\345\255\246\345\207\275\346\225\260/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\346\225\260\346\215\256\345\210\206\350\243\202/index.html" "b/tags/\346\225\260\346\215\256\345\210\206\350\243\202/index.html" index cac20a6f809bddd1ee803dbed683c9c955898e4e..9d13c2c2bfe4577b087b84eaef42c57b4fb8c944 100644 --- "a/tags/\346\225\260\346\215\256\345\210\206\350\243\202/index.html" +++ "b/tags/\346\225\260\346\215\256\345\210\206\350\243\202/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\346\225\260\346\215\256\345\217\257\350\247\206\345\214\226/index.html" "b/tags/\346\225\260\346\215\256\345\217\257\350\247\206\345\214\226/index.html" index 9d9c6303b4ca53b9beb66838cc57687448e5289c..eb5319d99c6eca7b16a275b96f33bc8720457101 100644 --- "a/tags/\346\225\260\346\215\256\345\217\257\350\247\206\345\214\226/index.html" +++ "b/tags/\346\225\260\346\215\256\345\217\257\350\247\206\345\214\226/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\346\225\260\346\215\256\345\220\210\345\271\266/index.html" "b/tags/\346\225\260\346\215\256\345\220\210\345\271\266/index.html" index 7cb594fd65301d7978c4396650714b829e45cb86..d0f919243be92f10f8cdb0f1f388b1c31bb6ab9e 100644 --- "a/tags/\346\225\260\346\215\256\345\220\210\345\271\266/index.html" +++ "b/tags/\346\225\260\346\215\256\345\220\210\345\271\266/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\346\225\260\346\215\256\346\233\277\346\215\242/index.html" "b/tags/\346\225\260\346\215\256\346\233\277\346\215\242/index.html" index 5e17ecd8769708217eab69f4b02bdfe476bf2532..18608fe95bff32d8c8197e569b2ec732c1d5f606 100644 --- "a/tags/\346\225\260\346\215\256\346\233\277\346\215\242/index.html" +++ "b/tags/\346\225\260\346\215\256\346\233\277\346\215\242/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\346\225\260\346\215\256\347\261\273\345\236\213/index.html" "b/tags/\346\225\260\346\215\256\347\261\273\345\236\213/index.html" index 0ca154c61b280e5f1980c031032e25e04d3d0097..a3e7703bbef7abb4a75ce17b687acc25bcd7c246 100644 --- "a/tags/\346\225\260\346\215\256\347\261\273\345\236\213/index.html" +++ "b/tags/\346\225\260\346\215\256\347\261\273\345\236\213/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\346\225\260\346\215\256\350\257\273\345\206\231/index.html" "b/tags/\346\225\260\346\215\256\350\257\273\345\206\231/index.html" index 08ffc65f89548c7d29220a97a1b8bb4d89f32574..6b71a6458fea225c37c447e9d6df4c3224a7995e 100644 --- "a/tags/\346\225\260\346\215\256\350\257\273\345\206\231/index.html" +++ "b/tags/\346\225\260\346\215\256\350\257\273\345\206\231/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\346\225\260\346\215\256\351\207\215\345\241\221/index.html" "b/tags/\346\225\260\346\215\256\351\207\215\345\241\221/index.html" index 96c329c31be565bca03d456829a260562d79e33c..8efe549437ffc6dfafd7a6259f39aa00f5598089 100644 --- "a/tags/\346\225\260\346\215\256\351\207\215\345\241\221/index.html" +++ "b/tags/\346\225\260\346\215\256\351\207\215\345\241\221/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\346\225\260\347\273\204/index.html" "b/tags/\346\225\260\347\273\204/index.html" index f3675cb2463612708a24aef8f1548ee4a3db8107..6c9d7d6b9c66a988079b961ede524065ab171ae2 100644 --- "a/tags/\346\225\260\347\273\204/index.html" +++ "b/tags/\346\225\260\347\273\204/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\346\226\207\344\273\266/index.html" "b/tags/\346\226\207\344\273\266/index.html" index 61ca2749b67a1c9c9b84ff374c886d197d0139c3..c3f928a2d4df0dd1eb0fd0a57c106d0ba9742e8a 100644 --- "a/tags/\346\226\207\344\273\266/index.html" +++ "b/tags/\346\226\207\344\273\266/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\346\226\207\344\273\266\345\202\250\345\255\230/index.html" "b/tags/\346\226\207\344\273\266\345\202\250\345\255\230/index.html" index 3c03667909c9894f711869bebd2e8192130a0af3..d9ab916a899ab36ac966502676410e8e406535a2 100644 --- "a/tags/\346\226\207\344\273\266\345\202\250\345\255\230/index.html" +++ "b/tags/\346\226\207\344\273\266\345\202\250\345\255\230/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\346\227\240\347\225\214\351\235\242\346\265\217\350\247\210\345\231\250/index.html" "b/tags/\346\227\240\347\225\214\351\235\242\346\265\217\350\247\210\345\231\250/index.html" index cb4d2ba9fa63fb5122c8584601365466b3208c22..712018e4b51a5034a27cc2730537ba9277ddf218 100644 --- "a/tags/\346\227\240\347\225\214\351\235\242\346\265\217\350\247\210\345\231\250/index.html" +++ "b/tags/\346\227\240\347\225\214\351\235\242\346\265\217\350\247\210\345\231\250/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\346\227\266\351\227\264\345\272\217\345\210\227/index.html" "b/tags/\346\227\266\351\227\264\345\272\217\345\210\227/index.html" index 42eea61ffbeb68b72cd9d5debe59b25f57967320..e1c5ba36fdbd376838497ff763ff2675266e5176 100644 --- "a/tags/\346\227\266\351\227\264\345\272\217\345\210\227/index.html" +++ "b/tags/\346\227\266\351\227\264\345\272\217\345\210\227/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\346\230\240\345\260\204/index.html" "b/tags/\346\230\240\345\260\204/index.html" index 4c44a1f2c81bdb9e5d822c9cc440d03a9b753148..fa64f332a40096073ae2c612fb20b2701af0a2a7 100644 --- "a/tags/\346\230\240\345\260\204/index.html" +++ "b/tags/\346\230\240\345\260\204/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\346\235\241\344\273\266\345\207\275\346\225\260/index.html" "b/tags/\346\235\241\344\273\266\345\207\275\346\225\260/index.html" index 62838a3eed982a61f91b6e810ca7e9c20d08fb90..b937d981a806289bf3f54e1edd6e401987d0aeb6 100644 --- "a/tags/\346\235\241\344\273\266\345\207\275\346\225\260/index.html" +++ "b/tags/\346\235\241\344\273\266\345\207\275\346\225\260/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\346\235\241\345\275\242\345\233\276/index.html" "b/tags/\346\235\241\345\275\242\345\233\276/index.html" index 2e99a9d7b7e462ab8ac973e34eefec714570f086..a06d3e01310f256899b7451583a4b8ffc36e686e 100644 --- "a/tags/\346\235\241\345\275\242\345\233\276/index.html" +++ "b/tags/\346\235\241\345\275\242\345\233\276/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\346\236\201\345\214\272\345\233\276/index.html" "b/tags/\346\236\201\345\214\272\345\233\276/index.html" index 15cbeb8116221d9ec5badae5075fce18dc7f4d18..20d6822685d070cb49cb79b7ecfb88850d0f5c0d 100644 --- "a/tags/\346\236\201\345\214\272\345\233\276/index.html" +++ "b/tags/\346\236\201\345\214\272\345\233\276/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\346\236\201\345\235\220\346\240\207\345\233\276/index.html" "b/tags/\346\236\201\345\235\220\346\240\207\345\233\276/index.html" index f8ab6b6ae9729cd94039238e4de64392ace47ec2..c197ca11bb2078f5c15c1edeb21df08700b1c60a 100644 --- "a/tags/\346\236\201\345\235\220\346\240\207\345\233\276/index.html" +++ "b/tags/\346\236\201\345\235\220\346\240\207\345\233\276/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\346\237\261\347\212\266\345\233\276/index.html" "b/tags/\346\237\261\347\212\266\345\233\276/index.html" index b10620f0379abaac4d6acef3e046e631e5c2faf8..19e44dc83fde7a095c00703c3fba6ff9fe6052fb 100644 --- "a/tags/\346\237\261\347\212\266\345\233\276/index.html" +++ "b/tags/\346\237\261\347\212\266\345\233\276/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\346\250\241\345\235\227/index.html" "b/tags/\346\250\241\345\235\227/index.html" index 2529db64724b4a46ee655a8e3f3ad6336c29e60c..ce52d8c907ed1acb7e6732ec03a7b95f59e001a1 100644 --- "a/tags/\346\250\241\345\235\227/index.html" +++ "b/tags/\346\250\241\345\235\227/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217/index.html" "b/tags/\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217/index.html" index 4eed159c45a20bfce75bd5204ad2c3e2ef47ddb1..fadfbe7d4d94629ab03fd4536750eac9934552da 100644 --- "a/tags/\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217/index.html" +++ "b/tags/\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\346\273\221\345\212\250\351\252\214\350\257\201\347\240\201/index.html" "b/tags/\346\273\221\345\212\250\351\252\214\350\257\201\347\240\201/index.html" index 8d3888be1dc7e2a9d6496eff657e4e76e7b3d150..33d4f09187fcf27b072da4be73736bffbe07e138 100644 --- "a/tags/\346\273\221\345\212\250\351\252\214\350\257\201\347\240\201/index.html" +++ "b/tags/\346\273\221\345\212\250\351\252\214\350\257\201\347\240\201/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\347\202\271\350\247\246\351\252\214\350\257\201\347\240\201/index.html" "b/tags/\347\202\271\350\247\246\351\252\214\350\257\201\347\240\201/index.html" index 00eac06f8b138961116979bea2a424e0a9e78d82..aa3c31cd1ea2abeeb9d42b8a602f0179411b8d58 100644 --- "a/tags/\347\202\271\350\247\246\351\252\214\350\257\201\347\240\201/index.html" +++ "b/tags/\347\202\271\350\247\246\351\252\214\350\257\201\347\240\201/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\347\210\254\350\231\253/index.html" "b/tags/\347\210\254\350\231\253/index.html" index 42c27fdfb607c8e0ddfa56a3dcc08188d55028ed..96d5f3284f83a9d968f0b8780610d7ada6579a44 100644 --- "a/tags/\347\210\254\350\231\253/index.html" +++ "b/tags/\347\210\254\350\231\253/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\347\210\254\350\231\253/page/2/index.html" "b/tags/\347\210\254\350\231\253/page/2/index.html" index 011f80c967605be42e16a9693ca3dd11b5ebfe78..214eeab56e1425523c0e2479fe26630724c0e60c 100644 --- "a/tags/\347\210\254\350\231\253/page/2/index.html" +++ "b/tags/\347\210\254\350\231\253/page/2/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\347\210\254\350\231\253/page/3/index.html" "b/tags/\347\210\254\350\231\253/page/3/index.html" index 17a747cdd231c1fd80a4348306c8fea02615eedb..93b8e2c822f0d9734a33097ef6b3ed45d2bdc51f 100644 --- "a/tags/\347\210\254\350\231\253/page/3/index.html" +++ "b/tags/\347\210\254\350\231\253/page/3/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\347\214\253\347\234\274\347\224\265\345\275\261/index.html" "b/tags/\347\214\253\347\234\274\347\224\265\345\275\261/index.html" index 301b40c1b5765d0c815efd2f906f270c026b43a1..988ffe9d574efd84333744bf53967c4256faed80 100644 --- "a/tags/\347\214\253\347\234\274\347\224\265\345\275\261/index.html" +++ "b/tags/\347\214\253\347\234\274\347\224\265\345\275\261/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\347\223\234\345\255\220\344\272\214\346\211\213\350\275\246/index.html" "b/tags/\347\223\234\345\255\220\344\272\214\346\211\213\350\275\246/index.html" index 888fe33afabda900f78c75007999ffbb1a93cfaf..b98d5d9be906e6b92e3f93396cf4537a91a6d17b 100644 --- "a/tags/\347\223\234\345\255\220\344\272\214\346\211\213\350\275\246/index.html" +++ "b/tags/\347\223\234\345\255\220\344\272\214\346\211\213\350\275\246/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\347\224\273\345\270\203/index.html" "b/tags/\347\224\273\345\270\203/index.html" index 9b669d561527f664c8368b8ed022d25b0589e653..5936a817cc0d77df7a6a5f7dbae3b617d3c4ad48 100644 --- "a/tags/\347\224\273\345\270\203/index.html" +++ "b/tags/\347\224\273\345\270\203/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\347\233\264\346\226\271\345\233\276/index.html" "b/tags/\347\233\264\346\226\271\345\233\276/index.html" index b18250a60cc113380bdcd2bed0b77afda18134cb..c2c89f62f536fe1ee8ad0f68d7f1c47ae4d3ae31 100644 --- "a/tags/\347\233\264\346\226\271\345\233\276/index.html" +++ "b/tags/\347\233\264\346\226\271\345\233\276/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\347\237\251\351\230\265/index.html" "b/tags/\347\237\251\351\230\265/index.html" index 96f71be448837e124a1ea69f334ec24ee7bf8e92..846ac7bf04155ed53bed2e1bda99f7f30aa65a58 100644 --- "a/tags/\347\237\251\351\230\265/index.html" +++ "b/tags/\347\237\251\351\230\265/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\347\255\211\345\200\274\347\272\277\345\233\276/index.html" "b/tags/\347\255\211\345\200\274\347\272\277\345\233\276/index.html" index 0778f7aae6a82bfd0a936114c388d18531abb5f9..c6eb35aa2de5ffdb7b9162cb534f0adc96928cfd 100644 --- "a/tags/\347\255\211\345\200\274\347\272\277\345\233\276/index.html" +++ "b/tags/\347\255\211\345\200\274\347\272\277\345\233\276/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\347\255\211\351\253\230\347\272\277\345\233\276/index.html" "b/tags/\347\255\211\351\253\230\347\272\277\345\233\276/index.html" index fe8f6fb9d5a3593d546a112805465286ca426b09..3557c08667a15bbf344e16ca176bc2a24b6e1997 100644 --- "a/tags/\347\255\211\351\253\230\347\272\277\345\233\276/index.html" +++ "b/tags/\347\255\211\351\253\230\347\272\277\345\233\276/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\347\256\227\346\234\257\345\207\275\346\225\260/index.html" "b/tags/\347\256\227\346\234\257\345\207\275\346\225\260/index.html" index 5dbad33f6800085c5a7fc2434a828d9e9e7cc357..efb051871adf67e9947805101cef41319755cd17 100644 --- "a/tags/\347\256\227\346\234\257\345\207\275\346\225\260/index.html" +++ "b/tags/\347\256\227\346\234\257\345\207\275\346\225\260/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\347\256\227\346\234\257\350\277\220\347\256\227/index.html" "b/tags/\347\256\227\346\234\257\350\277\220\347\256\227/index.html" index c24cb21bf0fb7f5f1ecad4e6c1e0f206aa59a6fc..65b04e2d0252c1734147b1b3cfccb782bde0dd62 100644 --- "a/tags/\347\256\227\346\234\257\350\277\220\347\256\227/index.html" +++ "b/tags/\347\256\227\346\234\257\350\277\220\347\256\227/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\347\261\273/index.html" "b/tags/\347\261\273/index.html" index eddab93cad479f12d8984f3608f258eef21b883a..16566b1ff2687713d64d0edf51f6bac18303113d 100644 --- "a/tags/\347\261\273/index.html" +++ "b/tags/\347\261\273/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\347\264\242\345\274\225/index.html" "b/tags/\347\264\242\345\274\225/index.html" index 869e334361d79bbd4d1dc5a658a9324d88a11b97..c3f072d1aa40b6d1e50b5cbd501b2812cfb10ac2 100644 --- "a/tags/\347\264\242\345\274\225/index.html" +++ "b/tags/\347\264\242\345\274\225/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\347\272\277\346\200\247\344\273\243\346\225\260/index.html" "b/tags/\347\272\277\346\200\247\344\273\243\346\225\260/index.html" index 3e26884ace5851e40d263073a8af9b0522c6976e..10d84f83ee554200811eaf5a470b7bae0c8f3f10 100644 --- "a/tags/\347\272\277\346\200\247\344\273\243\346\225\260/index.html" +++ "b/tags/\347\272\277\346\200\247\344\273\243\346\225\260/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\347\272\277\346\200\247\345\233\276/index.html" "b/tags/\347\272\277\346\200\247\345\233\276/index.html" index 8e8f7cefe5a9821d7f573df110168cdac951ad60..8d582ca6fc3adae2d70e24b852dc4f515c187ba1 100644 --- "a/tags/\347\272\277\346\200\247\345\233\276/index.html" +++ "b/tags/\347\272\277\346\200\247\345\233\276/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\347\273\237\350\256\241\345\207\275\346\225\260/index.html" "b/tags/\347\273\237\350\256\241\345\207\275\346\225\260/index.html" index 3b3d37c6f783f135e3fe42c027a5eecc8d846624..302198b7a83aec203aecac09bce79a6066369e47 100644 --- "a/tags/\347\273\237\350\256\241\345\207\275\346\225\260/index.html" +++ "b/tags/\347\273\237\350\256\241\345\207\275\346\225\260/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\347\273\237\350\256\241\346\217\217\350\277\260/index.html" "b/tags/\347\273\237\350\256\241\346\217\217\350\277\260/index.html" index 9539695e4c2c917a46b13bae4680ee80c891f834..da8c95b8fb38f44bf06644b2dcaf475a5b979f6c 100644 --- "a/tags/\347\273\237\350\256\241\346\217\217\350\277\260/index.html" +++ "b/tags/\347\273\237\350\256\241\346\217\217\350\277\260/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\347\273\237\350\256\241\350\256\241\347\256\227/index.html" "b/tags/\347\273\237\350\256\241\350\256\241\347\256\227/index.html" index d3b0f82a07c175115f7db03868a5777be80086cf..7b8f2622f93efb7202930216bfbf61a95ce7e49d 100644 --- "a/tags/\347\273\237\350\256\241\350\256\241\347\256\227/index.html" +++ "b/tags/\347\273\237\350\256\241\350\256\241\347\256\227/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\347\273\247\346\211\277/index.html" "b/tags/\347\273\247\346\211\277/index.html" index e0eeaa9ae7d608e9cbe2c8dd9d6d910a3213c625..7704b29fb4b2ee2556e6626f6021db49dfeed725 100644 --- "a/tags/\347\273\247\346\211\277/index.html" +++ "b/tags/\347\273\247\346\211\277/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\347\274\226\350\276\221\345\231\250/index.html" "b/tags/\347\274\226\350\276\221\345\231\250/index.html" index dc63256fd668da4b30c3b7eb23904d2d658e2138..cf19b4234d2cc613c13b0c57fdfe25c800c907de 100644 --- "a/tags/\347\274\226\350\276\221\345\231\250/index.html" +++ "b/tags/\347\274\226\350\276\221\345\231\250/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\347\274\272\345\244\261\345\200\274/index.html" "b/tags/\347\274\272\345\244\261\345\200\274/index.html" index 12a9117e1c2b820c27964bdaf99b00555f1e9e23..2a3949c59e1261d242264e70aa2c841a289ad46f 100644 --- "a/tags/\347\274\272\345\244\261\345\200\274/index.html" +++ "b/tags/\347\274\272\345\244\261\345\200\274/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\347\275\221\346\240\274/index.html" "b/tags/\347\275\221\346\240\274/index.html" index 83973a0590a57c3e3c405b42620b7109a898399b..6649f604a53d7fee298ecb2f106f6f4cc7f07a79 100644 --- "a/tags/\347\275\221\346\240\274/index.html" +++ "b/tags/\347\275\221\346\240\274/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\350\202\272\347\202\216\347\226\253\346\203\205/index.html" "b/tags/\350\202\272\347\202\216\347\226\253\346\203\205/index.html" index 223bb03a71efde33b9a43f29e79047bf4f8aab7a..22813c153a0d093f97d6e5052a98677257d5a625 100644 --- "a/tags/\350\202\272\347\202\216\347\226\253\346\203\205/index.html" +++ "b/tags/\350\202\272\347\202\216\347\226\253\346\203\205/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\350\231\216\346\211\221\350\256\272\345\235\233/index.html" "b/tags/\350\231\216\346\211\221\350\256\272\345\235\233/index.html" index 24999331b728d1a646ce3804b93e2e97ec17a347..2ceeb429b9041deb58f71c832f497f5c9b00d9d6 100644 --- "a/tags/\350\231\216\346\211\221\350\256\272\345\235\233/index.html" +++ "b/tags/\350\231\216\346\211\221\350\256\272\345\235\233/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\350\241\245\344\270\201/index.html" "b/tags/\350\241\245\344\270\201/index.html" index 08c92ec285c2edaad938c1d26bded4ddcd946ea4..3a4b41f61c94f90ba1b042b69626f3b1c2438842 100644 --- "a/tags/\350\241\245\344\270\201/index.html" +++ "b/tags/\350\241\245\344\270\201/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\350\261\206\347\223\243\347\224\265\345\275\261/index.html" "b/tags/\350\261\206\347\223\243\347\224\265\345\275\261/index.html" index 02349d7897ac4b762100b6b0aadaf4e08716bbef..e53b8f3c8e1789dd481d8f99ee0561e33d0aa9af 100644 --- "a/tags/\350\261\206\347\223\243\347\224\265\345\275\261/index.html" +++ "b/tags/\350\261\206\347\223\243\347\224\265\345\275\261/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\350\277\255\344\273\243/index.html" "b/tags/\350\277\255\344\273\243/index.html" index 3dd77c988933917e43cfc89e76545f4ce0cc7675..6bc2c03a9231ec258d3a89adf49474888410c05b 100644 --- "a/tags/\350\277\255\344\273\243/index.html" +++ "b/tags/\350\277\255\344\273\243/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\351\233\267\350\276\276\345\233\276/index.html" "b/tags/\351\233\267\350\276\276\345\233\276/index.html" index 9aa112c600b89d9d963e2c02d22362631dcd0e81..9c749ce217b7b2d62436136b8e36a82bd09cc3c2 100644 --- "a/tags/\351\233\267\350\276\276\345\233\276/index.html" +++ "b/tags/\351\233\267\350\276\276\345\233\276/index.html" @@ -110,7 +110,7 @@ - + diff --git "a/tags/\351\245\274\347\212\266\345\233\276/index.html" "b/tags/\351\245\274\347\212\266\345\233\276/index.html" index ffec9686e8c7b89650426281ece994fff4470d07..001766da79fc30beb6d16bc9b4d61f543e696612 100644 --- "a/tags/\351\245\274\347\212\266\345\233\276/index.html" +++ "b/tags/\351\245\274\347\212\266\345\233\276/index.html" @@ -110,7 +110,7 @@ - +