├── requirements.txt ├── .github └── workflows │ ├── love_heart_macos.yml │ ├── love_heart_ubuntu.yml │ ├── love_heart_windows.yml │ ├── daily_sign.yml │ ├── heart_break.yml │ ├── heart_break2.yml │ ├── wechat_msg.yml │ └── weather_report.yml ├── daily_sign.py ├── LICENSE ├── wechat_msg.py ├── README.md ├── heart_break2.py ├── heart_break.py ├── weather_report.py └── love_heart.py /requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | bs4 3 | html5lib 4 | -------------------------------------------------------------------------------- /.github/workflows/love_heart_macos.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a single version of Python 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python 3 | 4 | name: 画爱心macos版 5 | 6 | on: 7 | workflow_dispatch: 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | pyinstaller-build: 14 | runs-on: macos-latest 15 | steps: 16 | - name: Create Executable 17 | uses: sayyid5416/pyinstaller@v1 18 | with: 19 | python_ver: '3.12' 20 | spec: 'love_heart.py' 21 | upload_exe_with_name: 'love_heart' 22 | options: --onefile, --name "love_heart", --windowed, 23 | -------------------------------------------------------------------------------- /.github/workflows/love_heart_ubuntu.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a single version of Python 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python 3 | 4 | name: 画爱心Ubuntu版 5 | 6 | on: 7 | workflow_dispatch: 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | pyinstaller-build: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Create Executable 17 | uses: sayyid5416/pyinstaller@v1 18 | with: 19 | python_ver: '3.12' 20 | spec: 'love_heart.py' 21 | upload_exe_with_name: 'love_heart' 22 | options: --onefile, --name "love_heart", --windowed, 23 | -------------------------------------------------------------------------------- /.github/workflows/love_heart_windows.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a single version of Python 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python 3 | 4 | name: 画爱心Windows版 5 | 6 | on: 7 | workflow_dispatch: 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | pyinstaller-build: 14 | runs-on: windows-latest 15 | steps: 16 | - name: Create Executable 17 | uses: sayyid5416/pyinstaller@v1 18 | with: 19 | python_ver: '3.12' 20 | spec: 'love_heart.py' 21 | upload_exe_with_name: 'love_heart' 22 | options: --onefile, --name "love_heart", --windowed, 23 | -------------------------------------------------------------------------------- /.github/workflows/daily_sign.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a single version of Python 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python 3 | 4 | name: 签到薅羊毛 5 | 6 | on: 7 | schedule: 8 | # 此处是UTC时间,对应北京时间早八点 9 | - cron : '00 00 * * *' 10 | workflow_dispatch: 11 | 12 | permissions: 13 | contents: read 14 | 15 | jobs: 16 | build: 17 | 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v3 22 | - name: Set up Python 3.12 23 | uses: actions/setup-python@v3 24 | with: 25 | python-version: "3.12" 26 | - name: Install dependencies 27 | run: | 28 | python -m pip install --upgrade pip 29 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 30 | - name: Run weather Report 31 | run: | 32 | python daily_sign.py 33 | env: 34 | JD_COOKIE: ${{ secrets.JD_COOKIE }} 35 | -------------------------------------------------------------------------------- /daily_sign.py: -------------------------------------------------------------------------------- 1 | import os 2 | import requests 3 | 4 | cookie = os.environ.get("JD_COOKIE") 5 | 6 | url = ("https://api.m.jd.com/client.action?functionId=signBeanAct&body=%7B%22fp%22%3A%22-1%22%2C%22shshshfp%22%3A%22-1" 7 | "%22%2C%22shshshfpa%22%3A%22-1%22%2C%22referUrl%22%3A%22-1%22%2C%22userAgent%22%3A%22-1%22%2C%22jda%22%3A%22-1" 8 | "%22%2C%22rnVersion%22%3A%223.9%22%7D&appid=ld&client=apple&clientVersion=10.0.4&networkType=wifi&osVersion=14" 9 | ".8.1&uuid=3acd1f6361f86fc0a1bc23971b2e7bbe6197afb6&openudid=3acd1f6361f86fc0a1bc23971b2e7bbe6197afb6&jsonp" 10 | "=jsonp_1645885800574_58482") 11 | 12 | headers = {"Connection": 'keep-alive', 13 | "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", 14 | "Cache-Control": 'no-cache', 15 | "User-Agent": "okhttp/3.12.1;jdmall;android;version/10.3.4;build/92451;", 16 | "accept": "*/*", 17 | "connection": "Keep-Alive", 18 | "Accept-Encoding": "gzip,deflate", 19 | "Cookie": cookie 20 | } 21 | 22 | response = requests.post(url=url, headers=headers) 23 | print(response.text) 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 TechShrimp技术爬爬虾 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/workflows/heart_break.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a single version of Python 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python 3 | 4 | name: 心跳测试 5 | 6 | on: 7 | schedule: 8 | # 设置启动时间,为 UTC 时间, UTC23点 对应北京时间早7点 9 | - cron : '15 * * * *' 10 | workflow_dispatch: 11 | 12 | permissions: 13 | contents: read 14 | 15 | jobs: 16 | build: 17 | 18 | runs-on: ubuntu-latest 19 | env: 20 | TZ: Asia/Shanghai 21 | steps: 22 | - uses: actions/checkout@v3 23 | - name: Set up Python 3.12 24 | uses: actions/setup-python@v3 25 | with: 26 | python-version: "3.12" 27 | - name: Install dependencies 28 | run: | 29 | python -m pip install --upgrade pip 30 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 31 | - name: Run heart break 32 | run: | 33 | python heart_break.py 34 | env: 35 | TARGET_URL: ${{ secrets.TARGET_URL }} 36 | ORIGIN_URL: ${{ secrets.ORIGIN_URL }} 37 | REFERER_URL: ${{ secrets.REFERER_URL }} 38 | -------------------------------------------------------------------------------- /.github/workflows/heart_break2.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a single version of Python 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python 3 | 4 | name: 心跳测试2 5 | 6 | on: 7 | schedule: 8 | # 设置启动时间,为 UTC 时间, UTC23点 对应北京时间早7点 9 | - cron : '45 * * * *' 10 | workflow_dispatch: 11 | 12 | permissions: 13 | contents: read 14 | 15 | jobs: 16 | build: 17 | 18 | runs-on: ubuntu-latest 19 | env: 20 | TZ: Asia/Shanghai 21 | steps: 22 | - uses: actions/checkout@v3 23 | - name: Set up Python 3.12 24 | uses: actions/setup-python@v3 25 | with: 26 | python-version: "3.12" 27 | - name: Install dependencies 28 | run: | 29 | python -m pip install --upgrade pip 30 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 31 | - name: Run heart break 2 32 | run: | 33 | python heart_break2.py 34 | env: 35 | TARGET_URL2: ${{ secrets.TARGET_URL2 }} 36 | ORIGIN_URL: ${{ secrets.ORIGIN_URL }} 37 | REFERER_URL: ${{ secrets.REFERER_URL }} 38 | -------------------------------------------------------------------------------- /.github/workflows/wechat_msg.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a single version of Python 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python 3 | 4 | name: 微信消息推送 5 | 6 | on: 7 | schedule: 8 | # 设置启动时间,为 UTC 时间, UTC23点 对应北京时间早7点 9 | - cron : '0 5 * * *' 10 | workflow_dispatch: 11 | 12 | permissions: 13 | contents: read 14 | 15 | jobs: 16 | build: 17 | 18 | runs-on: ubuntu-latest 19 | env: 20 | TZ: Asia/Shanghai 21 | steps: 22 | - uses: actions/checkout@v3 23 | - name: Set up Python 3.12 24 | uses: actions/setup-python@v3 25 | with: 26 | python-version: "3.12" 27 | - name: Install dependencies 28 | run: | 29 | python -m pip install --upgrade pip 30 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 31 | - name: Run wechat send message 32 | run: | 33 | python wechat_msg.py 34 | env: 35 | USERID: ${{ secrets.USERID }} 36 | AGENTID: ${{ secrets.AGENTID }} 37 | CORPID: ${{ secrets.CORPID }} 38 | CORPSECRET: ${{ secrets.CORPSECRET }} 39 | -------------------------------------------------------------------------------- /.github/workflows/weather_report.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a single version of Python 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python 3 | 4 | name: 天气预报推送 5 | 6 | on: 7 | schedule: 8 | # 设置启动时间,为 UTC 时间, UTC23点 对应北京时间早7点 9 | - cron : '00 23 * * *' 10 | workflow_dispatch: 11 | 12 | permissions: 13 | contents: read 14 | 15 | jobs: 16 | build: 17 | 18 | runs-on: ubuntu-latest 19 | env: 20 | TZ: Asia/Shanghai 21 | steps: 22 | - uses: actions/checkout@v3 23 | - name: Set up Python 3.12 24 | uses: actions/setup-python@v3 25 | with: 26 | python-version: "3.12" 27 | - name: Install dependencies 28 | run: | 29 | python -m pip install --upgrade pip 30 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 31 | - name: Run weather Report 32 | run: | 33 | python weather_report.py 34 | env: 35 | APP_ID: ${{ secrets.APP_ID }} 36 | APP_SECRET: ${{ secrets.APP_SECRET }} 37 | OPEN_ID: ${{ secrets.OPEN_ID }} 38 | TEMPLATE_ID: ${{ secrets.TEMPLATE_ID }} 39 | -------------------------------------------------------------------------------- /wechat_msg.py: -------------------------------------------------------------------------------- 1 | # 安装依赖 pip3 install requests html5lib bs4 schedule 2 | import os 3 | import requests 4 | import json 5 | import datetime 6 | 7 | # 从测试号信息获取 8 | userid = os.environ.get("USERID") 9 | agentid = os.environ.get("AGENTID") 10 | corpid = os.environ.get("CORPID") 11 | corpsecret = os.environ.get("CORPSECRET") 12 | 13 | 14 | 15 | 16 | class WeChatNotify: 17 | 18 | def __init__(self): 19 | pass 20 | 21 | def send_message_via_wechat(self, _message): # 默认发送给自己 22 | 23 | response = requests.get( 24 | f"https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={corpid}&corpsecret={corpsecret}") 25 | data = json.loads(response.text) 26 | access_token = data['access_token'] 27 | 28 | json_dict = { 29 | "touser": userid, 30 | "msgtype": "text", 31 | "agentid": agentid, 32 | "text": { 33 | "content": _message 34 | }, 35 | "safe": 0, 36 | "enable_id_trans": 0, 37 | "enable_duplicate_check": 0, 38 | "duplicate_check_interval": 1800 39 | } 40 | json_str = json.dumps(json_dict) 41 | response_send = requests.post(f"https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token={access_token}", 42 | data=json_str) 43 | return json.loads(response_send.text)['errmsg'] == 'ok' 44 | 45 | 46 | 47 | def main(): 48 | wechat = WeChatNotify() 49 | wechat.send_message_via_wechat("Hello, World! From github actions.{}".format(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))) 50 | 51 | if __name__ == '__main__': 52 | main() -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Github Action功能样例 2 | 3 | 原理:使用Github Action功能,运行python程序,实现无服务器的免费任务,比如天气推送,薅羊毛,签到 4 | 5 | 6 | ## Part1 构建画爱心为可执行程序 7 | Fork本项目 8 | 9 | 构架Windows 可执行程序: 10 | Actions-->画爱心Windows版-->run work flow-->结束后查看结果 11 | -->Artifacts-->下载love_heart 12 | 13 | 构架Ubuntu 可执行程序: 14 | Actions-->画爱心Ubuntu版-->run work flow-->结束后查看结果 15 | -->Artifacts-->下载love_heart 16 | 17 | 构架MacOS 可执行程序: 18 | Actions-->画爱心MacOS版-->run work flow-->结束后查看结果 19 | -->Artifacts-->下载love_heart 20 | 21 | 22 | ## Part2 天气推送 23 | 24 | ### 申请公众号测试账户 25 | 26 | 使用微信扫码即可 27 | https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login 28 | 29 | 进入页面以后我们来获取到这四个值 30 | #### appID appSecret openId template_id 31 | ![image](https://github.com/tech-shrimp/FreeWechatPush/assets/154193368/bdb27abd-39cb-4e77-9b89-299afabc7330) 32 | 33 | 想让谁收消息,谁就用微信扫二维码,然后出现在用户列表,获取微信号(openId) 34 | ![image](https://github.com/tech-shrimp/FreeWechatPush/assets/154193368/1327c6f5-5c92-4310-a10b-6f2956c1dd75) 35 | 36 | 新增测试模板获得 template_id(模板ID) 37 | ![image](https://github.com/tech-shrimp/FreeWechatPush/assets/154193368/ec689f4d-6c0b-44c4-915a-6fd7ada17028) 38 | 39 | 模板标题随便填,模板内容如下,可以根据需求自己定制 40 | 41 | 模板内容: 42 | ```copy 43 | 今天:{{date.DATA}} 44 | 地区:{{region.DATA}} 45 | 天气:{{weather.DATA}} 46 | 气温:{{temp.DATA}} 47 | 风向:{{wind_dir.DATA}} 48 | 对你说的话:{{today_note.DATA}} 49 | ``` 50 | 51 | ### 项目配置 52 | Fork本项目 53 | 进入自己项目的Settings ----> Secrets and variables ---> Actions --> New repository secret 54 | 配置好以下四个值(见上文) 55 | 56 | image 57 | 58 | 进入自己项目的Action ----> 天气预报推送 ---> weather_report.yml --> 修改cron表达式的执行时间 59 | image 60 | 61 | ## Part3 签到薅羊毛 62 | Fork本项目 63 | 网页上打开:www.jd.com/ 再按F12打开控制台,再点击切换模式,切换到手机模式,刷新一下页面。如图所示 64 | ![image](https://github.com/tech-shrimp/GithubActionSample/assets/154193368/44d01795-8c1e-4a56-bb0e-a36f74062dcb) 65 | 在网络->m.jd.com找到Cookie 66 | 67 | image 68 | 69 | 将其填入 Settings ----> Secrets and variables ---> Actions --> New repository secret -->新增JD_COOKIE 70 | image 71 | 72 | 进入自己项目的Action ----> 签到薅羊毛 ---> daily_sign.yml --> 修改cron表达式的执行时间 73 | 74 | ## Part 4 定时发送微信消息 75 | 76 | wechat_msg.py 77 | 78 | 设定cronjob时间,发布微信消息 -------------------------------------------------------------------------------- /heart_break2.py: -------------------------------------------------------------------------------- 1 | import threading 2 | import requests 3 | import json 4 | import os 5 | 6 | url = os.environ.get('TARGET_URL2') 7 | ORIGIN_URL = os.environ.get("ORIGIN_URL") 8 | REFERER_URL = os.environ.get("REFERER_URL") 9 | 10 | 11 | payload = json.dumps({ 12 | "output": "..graph.figure...grid-result-detail-table.data...grid-eval-text.children..", 13 | "outputs": [ 14 | { 15 | "id": "graph", 16 | "property": "figure" 17 | }, 18 | { 19 | "id": "grid-result-detail-table", 20 | "property": "data" 21 | }, 22 | { 23 | "id": "grid-eval-text", 24 | "property": "children" 25 | } 26 | ], 27 | "inputs": [ 28 | { 29 | "id": "grid-button", 30 | "property": "nClicks", 31 | "value": 2 32 | } 33 | ], 34 | "changedPropIds": [ 35 | "grid-button.nClicks" 36 | ], 37 | "state": [ 38 | { 39 | "id": "grid-code", 40 | "property": "value", 41 | "value": "128143" 42 | }, 43 | { 44 | "id": "date-picker-range", 45 | "property": "start_date", 46 | "value": "2021-08-15" 47 | }, 48 | { 49 | "id": "date-picker-range", 50 | "property": "end_date", 51 | "value": "2022-08-15" 52 | }, 53 | { 54 | "id": "grid-count", 55 | "property": "value", 56 | "value": 20 57 | }, 58 | { 59 | "id": "grid-type", 60 | "property": "value", 61 | "value": "line" 62 | }, 63 | { 64 | "id": "grid-c-rate", 65 | "property": "value", 66 | "value": 0.00005 67 | }, 68 | { 69 | "id": "stop-on-top", 70 | "property": "value" 71 | }, 72 | { 73 | "id": "grid-range", 74 | "property": "value", 75 | "value": [ 76 | 100, 77 | 140 78 | ] 79 | } 80 | ] 81 | }) 82 | headers = { 83 | 'accept': 'application/json', 84 | 'accept-language': 'zh,en;q=0.9,en-US;q=0.8,zh-CN;q=0.7,zh-TW;q=0.6', 85 | 'cache-control': 'no-cache', 86 | 'content-type': 'application/json', 87 | 'cookie': 'sl-session=Xnyib1wdeWcfqqilNWwh3g==; Hm_lvt_77cc3540511b0ae5a479b7d7f0994547=1733414758,1735904241; HMACCOUNT=8144A39C4F4A22E9; Hm_lpvt_77cc3540511b0ae5a479b7d7f0994547=1735906918; session=.eJyrVspMSc0rySyp1EssLcmIL6ksSFWyyivNydFByGSmQIRqAZu4EWc.Z3fsqQ.NUeLqedmtGwo-_trvaEisBQip80; session=.eJyrVspMSc0rySyp1EssLcmIL6ksSFWyyivNydFByGSmQIRqAZu4EWc.Z3fXHA.FuMb2586X2komaVS7gHtc551DSM', 88 | 'origin': ORIGIN_URL, 89 | 'pragma': 'no-cache', 90 | 'priority': 'u=1, i', 91 | 'referer': REFERER_URL, 92 | 'sec-ch-ua': '"Not/A)Brand";v="8", "Chromium";v="126", "Google Chrome";v="126"', 93 | 'sec-ch-ua-mobile': '?0', 94 | 'sec-ch-ua-platform': '"Windows"', 95 | 'sec-fetch-dest': 'empty', 96 | 'sec-fetch-mode': 'cors', 97 | 'sec-fetch-site': 'same-origin', 98 | 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36', 99 | 'x-csrftoken': 'undefined' 100 | } 101 | 102 | 103 | def send_request(): 104 | try: 105 | response = requests.request( 106 | "POST", url, headers=headers, data=payload, timeout=10) 107 | print(response.text) 108 | except Exception as e: 109 | print(e) 110 | 111 | 112 | thread_list = [] 113 | for i in range(100): 114 | t = threading.Thread(target=send_request) 115 | t.start() 116 | thread_list.append(t) 117 | 118 | 119 | for t in thread_list: 120 | t.join() 121 | 122 | print("done") 123 | -------------------------------------------------------------------------------- /heart_break.py: -------------------------------------------------------------------------------- 1 | import threading 2 | import requests 3 | import json 4 | import os 5 | import time 6 | 7 | url = os.environ.get('TARGET_URL') 8 | ORIGIN_URL = os.environ.get("ORIGIN_URL") 9 | REFERER_URL = os.environ.get("REFERER_URL") 10 | 11 | payload = json.dumps({ 12 | "output": "market-board-grid-container.children", 13 | "outputs": { 14 | "id": "market-board-grid-container", 15 | "property": "children" 16 | }, 17 | "inputs": [ 18 | { 19 | "id": "market-board-layouts-restore", 20 | "property": "data", 21 | "value": [ 22 | { 23 | "w": 12, 24 | "h": 6, 25 | "x": 0, 26 | "y": 0, 27 | "i": "scope_graph", 28 | "moved": False, 29 | "static": False 30 | }, 31 | { 32 | "w": 6, 33 | "h": 4, 34 | "x": 0, 35 | "y": 6, 36 | "i": "up_or_down_graph", 37 | "moved": False, 38 | "static": False 39 | }, 40 | { 41 | "w": 6, 42 | "h": 4, 43 | "x": 6, 44 | "y": 6, 45 | "i": "agg_table", 46 | "moved": False, 47 | "static": False 48 | }, 49 | { 50 | "w": 12, 51 | "h": 5, 52 | "x": 0, 53 | "y": 10, 54 | "i": "treemap", 55 | "moved": False, 56 | "static": False 57 | } 58 | ] 59 | } 60 | ], 61 | "changedPropIds": [ 62 | "market-board-layouts-restore.data" 63 | ] 64 | }) 65 | 66 | headers = { 67 | 'accept': 'application/json', 68 | 'accept-language': 'zh,en;q=0.9,en-US;q=0.8,zh-CN;q=0.7,zh-TW;q=0.6', 69 | 'cache-control': 'no-cache', 70 | 'content-type': 'application/json', 71 | 'cookie': 'sl-session=Xnyib1wdeWcfqqilNWwh3g==; Hm_lvt_77cc3540511b0ae5a479b7d7f0994547=1733414758,1735904241; HMACCOUNT=8144A39C4F4A22E9; Hm_lpvt_77cc3540511b0ae5a479b7d7f0994547=1735906918; session=.eJyrVspMSc0rySyp1EssLcmIL6ksSFWyyivNydFByGSmQIRqAZu4EWc.Z3fWUg.dnGBgW2aZwgaPYStmdMb6r2xngE; session=.eJyrVspMSc0rySyp1EssLcmIL6ksSFWyyivNydFByGSmQIRqAZu4EWc.Z3fXHA.FuMb2586X2komaVS7gHtc551DSM', 72 | 'origin': ORIGIN_URL, 73 | 'pragma': 'no-cache', 74 | 'priority': 'u=1, i', 75 | 'referer': REFERER_URL, 76 | 'sec-ch-ua': '"Not/A)Brand";v="8", "Chromium";v="126", "Google Chrome";v="126"', 77 | 'sec-ch-ua-mobile': '?0', 78 | 'sec-ch-ua-platform': '"Windows"', 79 | 'sec-fetch-dest': 'empty', 80 | 'sec-fetch-mode': 'cors', 81 | 'sec-fetch-site': 'same-origin', 82 | 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36', 83 | 'x-csrftoken': 'undefined' 84 | } 85 | 86 | 87 | def send_request(): 88 | try: 89 | response = requests.request( 90 | "POST", url, headers=headers, data=payload, timeout=10) 91 | print(response.text) 92 | except Exception as e: 93 | print(e) 94 | 95 | 96 | thread_list = [] 97 | for i in range(100): 98 | t = threading.Thread(target=send_request) 99 | t.start() 100 | thread_list.append(t) 101 | 102 | 103 | for t in thread_list: 104 | t.join() 105 | print("done") 106 | -------------------------------------------------------------------------------- /weather_report.py: -------------------------------------------------------------------------------- 1 | # 安装依赖 pip3 install requests html5lib bs4 schedule 2 | import os 3 | import requests 4 | import json 5 | from bs4 import BeautifulSoup 6 | 7 | # 从测试号信息获取 8 | appID = os.environ.get("APP_ID") 9 | appSecret = os.environ.get("APP_SECRET") 10 | # 收信人ID即 用户列表中的微信号 11 | openId = os.environ.get("OPEN_ID") 12 | # 天气预报模板ID 13 | weather_template_id = os.environ.get("TEMPLATE_ID") 14 | 15 | def get_weather(my_city): 16 | urls = ["http://www.weather.com.cn/textFC/hb.shtml", 17 | "http://www.weather.com.cn/textFC/db.shtml", 18 | "http://www.weather.com.cn/textFC/hd.shtml", 19 | "http://www.weather.com.cn/textFC/hz.shtml", 20 | "http://www.weather.com.cn/textFC/hn.shtml", 21 | "http://www.weather.com.cn/textFC/xb.shtml", 22 | "http://www.weather.com.cn/textFC/xn.shtml" 23 | ] 24 | for url in urls: 25 | resp = requests.get(url) 26 | text = resp.content.decode("utf-8") 27 | soup = BeautifulSoup(text, 'html5lib') 28 | div_conMidtab = soup.find("div", class_="conMidtab") 29 | tables = div_conMidtab.find_all("table") 30 | for table in tables: 31 | trs = table.find_all("tr")[2:] 32 | for index, tr in enumerate(trs): 33 | tds = tr.find_all("td") 34 | # 这里倒着数,因为每个省会的td结构跟其他不一样 35 | city_td = tds[-8] 36 | this_city = list(city_td.stripped_strings)[0] 37 | if this_city == my_city: 38 | 39 | high_temp_td = tds[-5] 40 | low_temp_td = tds[-2] 41 | weather_type_day_td = tds[-7] 42 | weather_type_night_td = tds[-4] 43 | wind_td_day = tds[-6] 44 | wind_td_day_night = tds[-3] 45 | 46 | high_temp = list(high_temp_td.stripped_strings)[0] 47 | low_temp = list(low_temp_td.stripped_strings)[0] 48 | weather_typ_day = list(weather_type_day_td.stripped_strings)[0] 49 | weather_type_night = list(weather_type_night_td.stripped_strings)[0] 50 | 51 | wind_day = list(wind_td_day.stripped_strings)[0] + list(wind_td_day.stripped_strings)[1] 52 | wind_night = list(wind_td_day_night.stripped_strings)[0] + list(wind_td_day_night.stripped_strings)[1] 53 | 54 | # 如果没有白天的数据就使用夜间的 55 | temp = f"{low_temp}——{high_temp}摄氏度" if high_temp != "-" else f"{low_temp}摄氏度" 56 | weather_typ = weather_typ_day if weather_typ_day != "-" else weather_type_night 57 | wind = f"{wind_day}" if wind_day != "--" else f"{wind_night}" 58 | return this_city, temp, weather_typ, wind 59 | 60 | 61 | def get_access_token(): 62 | # 获取access token的url 63 | url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={}&secret={}' \ 64 | .format(appID.strip(), appSecret.strip()) 65 | response = requests.get(url).json() 66 | print(response) 67 | access_token = response.get('access_token') 68 | return access_token 69 | 70 | 71 | def get_daily_love(): 72 | # 每日一句情话 73 | url = "https://api.lovelive.tools/api/SweetNothings/Serialization/Json" 74 | r = requests.get(url) 75 | all_dict = json.loads(r.text) 76 | sentence = all_dict['returnObj'][0] 77 | daily_love = sentence 78 | return daily_love 79 | 80 | 81 | def send_weather(access_token, weather): 82 | # touser 就是 openID 83 | # template_id 就是模板ID 84 | # url 就是点击模板跳转的url 85 | # data就按这种格式写,time和text就是之前{{time.DATA}}中的那个time,value就是你要替换DATA的值 86 | 87 | import datetime 88 | today = datetime.date.today() 89 | today_str = today.strftime("%Y年%m月%d日") 90 | 91 | body = { 92 | "touser": openId.strip(), 93 | "template_id": weather_template_id.strip(), 94 | "url": "https://weixin.qq.com", 95 | "data": { 96 | "date": { 97 | "value": today_str 98 | }, 99 | "region": { 100 | "value": weather[0] 101 | }, 102 | "weather": { 103 | "value": weather[2] 104 | }, 105 | "temp": { 106 | "value": weather[1] 107 | }, 108 | "wind_dir": { 109 | "value": weather[3] 110 | }, 111 | "today_note": { 112 | "value": get_daily_love() 113 | } 114 | } 115 | } 116 | url = 'https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={}'.format(access_token) 117 | print(requests.post(url, json.dumps(body)).text) 118 | 119 | 120 | 121 | def weather_report(this_city): 122 | # 1.获取access_token 123 | access_token = get_access_token() 124 | # 2. 获取天气 125 | weather = get_weather(this_city) 126 | print(f"天气信息: {weather}") 127 | # 3. 发送消息 128 | send_weather(access_token, weather) 129 | 130 | 131 | 132 | if __name__ == '__main__': 133 | weather_report("淄博") -------------------------------------------------------------------------------- /love_heart.py: -------------------------------------------------------------------------------- 1 | # 版权https://github.com/royalneverwin/beating-heart 2 | 3 | from tkinter import * # Python 实现GUI界面的包 4 | from math import sin, cos, pi, log 5 | import random 6 | import time 7 | 8 | CANVAS_WIDTH = 640 9 | CANVAS_HEIGHT = 480 10 | CANVAS_CENTER_X = CANVAS_WIDTH / 2 11 | CANVAS_CENTER_Y = CANVAS_HEIGHT / 2 12 | IMAGE_ENLARGE = 11 13 | 14 | 15 | def scatter_inside(x, y, beta=0.15): # log scatter & scatter inside 16 | ratiox = - beta * log(random.random()) #*** can modify ***# 17 | ratioy = - beta * log(random.random()) 18 | dx = ratiox * (x - CANVAS_CENTER_X) 19 | dy = ratioy * (y - CANVAS_CENTER_Y) 20 | return x - dx, y - dy 21 | 22 | 23 | def heart_function(t, enlarge_ratio: float = IMAGE_ENLARGE): 24 | # heart function 25 | x = 16 * (sin(t)**3) 26 | y = -(13 * cos(t) - 5 * cos(2*t) - 2 * cos(3*t) - cos(4*t)) 27 | 28 | # enlarge 29 | x *= enlarge_ratio 30 | y *= enlarge_ratio 31 | 32 | # shift to the center of canvas 33 | x += CANVAS_CENTER_X 34 | y += CANVAS_CENTER_Y 35 | 36 | return int(x), int(y) 37 | 38 | def shrink(x, y, ratio): 39 | sk_range = -1 / ((x-CANVAS_CENTER_X) ** 2 + (y-CANVAS_CENTER_Y) ** 2) 40 | dx = ratio * sk_range * (x-CANVAS_CENTER_X) 41 | dy = ratio * sk_range * (y-CANVAS_CENTER_Y) 42 | return x - dx, y - dy 43 | 44 | 45 | class Heart: 46 | def __init__(self, frame): 47 | self.points = set() 48 | self.edge_points = set() 49 | self.inside_points = set() 50 | self.all_points = {} 51 | self.build(2000) #*** can modify ***# 52 | self.frame = frame 53 | for f in range(frame): # pre calculate 54 | self.calc(f) 55 | 56 | # for halo 57 | self.random_halo = 1000 58 | 59 | 60 | 61 | def build(self, number): 62 | # randomly find 'number' points on the heart curve 63 | for _ in range(number): 64 | t = random.uniform(0, 2 * pi) # t = angle 65 | x, y = heart_function(t) 66 | x, y = shrink(x, y, -1000) 67 | self.points.add((int(x), int(y))) 68 | 69 | # randomly find points on the edge 70 | for px, py in self.points: 71 | for _ in range(3): #*** can modify ***# 72 | x, y = scatter_inside(px, py, 0.05) #*** can modify ***# 73 | self.edge_points.add((x, y)) 74 | 75 | # randomly find points inside the heart 76 | pt_ls = list(self.points) 77 | for _ in range(4000): #*** can modify ***# 78 | x, y = random.choice(pt_ls) # choice need idx, and set has no idx, only list has 79 | x, y = scatter_inside(x, y) #*** can modify ***# 80 | self.inside_points.add((x, y)) 81 | 82 | 83 | def cal_position(self, x, y, ratio): # calculate the position of points when beating 84 | # attention: the closer to the center, the bigger beating range point has 85 | bt_range = 1 / ((x-CANVAS_CENTER_X) ** 2 + (y-CANVAS_CENTER_Y) ** 2) 86 | dx = ratio * bt_range * (x-CANVAS_CENTER_X) + random.randint(-1, 1) 87 | dy = ratio * bt_range * (y-CANVAS_CENTER_Y) + random.randint(-1, 1) 88 | return x - dx, y - dy 89 | 90 | 91 | def calc(self, frame): # calculate points' position for different frame 92 | ratio = 800 * sin(frame / 10 * pi) #*** can modify ***# this is 30 fps 93 | all_pts = [] 94 | 95 | # for halo 96 | halo_radius = int(4 + 6 * (1 + sin(self.frame / 10 * pi))) 97 | halo_number = int(3000 + 4000 * abs(sin(self.frame / 10 * pi) ** 2)) 98 | heart_halo_point = set() 99 | for _ in range(halo_number): 100 | t = random.uniform(0, 2 * pi) 101 | x, y = heart_function(t, enlarge_ratio=11.6) 102 | x, y = shrink(x, y, halo_radius) 103 | if (x, y) not in heart_halo_point: 104 | # 处理新的点 105 | heart_halo_point.add((x, y)) 106 | x += random.randint(-14, 14) 107 | y += random.randint(-14, 14) 108 | size = random.choice((1, 2, 2)) 109 | all_pts.append((x, y, size)) 110 | 111 | # on the curve 112 | for x, y in self.points: 113 | x, y = self.cal_position(x, y, ratio) 114 | size = random.randint(1, 3) #*** can modify ***# 115 | all_pts.append((x, y, size)) 116 | 117 | # on the edge 118 | for x, y in self.edge_points: 119 | x, y = self.cal_position(x, y, ratio) 120 | size = random.randint(1, 2) #*** can modify ***# 121 | all_pts.append((x, y, size)) 122 | 123 | # inside 124 | for x, y in self.inside_points: 125 | x, y = self.cal_position(x, y, ratio) 126 | size = random.randint(1, 2) #*** can modify ***# 127 | all_pts.append((x, y, size)) 128 | 129 | self.all_points[frame] = all_pts 130 | 131 | 132 | def render(self, canvas, frame): # draw points 133 | for x, y, size in self.all_points[frame % self.frame]: # set operation 134 | canvas.create_rectangle(x, y, x+size, y+size, width=0, fill='#ff7171') 135 | 136 | 137 | def draw(root: Tk, canvas: Canvas, heart: Heart, frame=0): 138 | canvas.delete('all') 139 | heart.render(canvas, frame) 140 | root.after(30, draw, root, canvas, heart, frame+1) 141 | 142 | 143 | if __name__ == '__main__': 144 | root = Tk() 145 | root.title('漂亮宝贝一周年快乐') 146 | canvas = Canvas(root, bg='black', height=CANVAS_HEIGHT, width=CANVAS_WIDTH) 147 | canvas.pack() 148 | heart = Heart(20) 149 | draw(root, canvas, heart) 150 | root.mainloop() --------------------------------------------------------------------------------