{ "info": { "author": "Fiona Pigott, Jeff Kolb, Josh Montague, Aaron Gonzales", "author_email": "agonzales@twitter.com", "bugtrack_url": null, "classifiers": [], "description": "Python Twitter Search API\n=========================\n\nThis project serves as a wrapper for the `Twitter premium and enterprise\nsearch\nAPIs `__,\nproviding a command-line utility and a Python library. Pretty docs can\nbe seen `here `__.\n\nFeatures\n========\n\n- Supports 30-day Search and Full Archive Search (not the standard\n Search API at this time).\n- Command-line utility is pipeable to other tools (e.g., ``jq``).\n- Automatically handles pagination of search results with specifiable\n limits\n- Delivers a stream of data to the user for low in-memory requirements\n- Handles enterprise and premium authentication methods\n- Flexible usage within a python program\n- Compatible with our group's `Tweet\n Parser `__ for rapid\n extraction of relevant data fields from each tweet payload\n- Supports the Search Counts endpoint, which can reduce API call usage\n and provide rapid insights if you only need Tweet volumes and not\n Tweet payloads\n\nInstallation\n============\n\nThe ``searchtweets`` library is on Pypi:\n\n.. code:: bash\n\n pip install searchtweets\n\nOr you can install the development version locally via\n\n.. code:: bash\n\n git clone https://github.com/twitterdev/search-tweets-python\n cd search-tweets-python\n pip install -e .\n\n--------------\n\nCredential Handling\n===================\n\nThe premium and enterprise Search APIs use different authentication\nmethods and we attempt to provide a seamless way to handle\nauthentication for all customers. We know credentials can be tricking or\nannoying - please read this in its entirety.\n\nPremium clients will require the ``bearer_token`` and ``endpoint``\nfields; Enterprise clients require ``username``, ``password``, and\n``endpoint``. If you do not specify the ``account_type``, we attempt to\ndiscern the account type and declare a warning about this behavior.\n\nFor premium search products, we are using app-only authentication and\nthe bearer tokens are not delivered with an expiration time. You can\nprovide either: - your application key and secret (the library will\nhandle bearer-token authentication) - a bearer token that you get\nyourself\n\nMany developers might find providing your application key and secret\nmore straightforward and letting this library manage your bearer token\ngeneration for you. Please see\n`here `__\nfor an overview of the premium authentication method.\n\nWe support both YAML-file based methods and environment variables for\nstoring credentials, and provide flexible handling with sensible\ndefaults.\n\nYAML method\n-----------\n\nFor premium customers, the simplest credential file should look like\nthis:\n\n.. code:: yaml\n\n search_tweets_api:\n account_type: premium\n endpoint: \n consumer_key: \n consumer_secret: \n\nFor enterprise customers, the simplest credential file should look like\nthis:\n\n.. code:: yaml\n\n search_tweets_api:\n account_type: enterprise\n endpoint: \n username: \n password: \n\nBy default, this library expects this file at\n``\"~/.twitter_keys.yaml\"``, but you can pass the relevant location as\nneeded, either with the ``--credential-file`` flag for the command-line\napp or as demonstrated below in a Python program.\n\nBoth above examples require no special command-line arguments or\nin-program arguments. The credential parsing methods, unless otherwise\nspecified, will look for a YAML key called ``search_tweets_api``.\n\nFor developers who have multiple endpoints and/or search products, you\ncan keep all credentials in the same file and specify specific keys to\nuse. ``--credential-file-key`` specifies this behavior in the command\nline app. An example:\n\n.. code:: yaml\n\n search_tweets_30_day_dev:\n account_type: premium\n endpoint: \n consumer_key: \n consumer_secret: \n (optional) bearer_token: \n\n\n search_tweets_30_day_prod:\n account_type: premium\n endpoint: \n bearer_token: \n\n search_tweets_fullarchive_dev:\n account_type: premium\n endpoint: \n bearer_token: \n\n search_tweets_fullarchive_prod:\n account_type: premium\n endpoint: \n bearer_token: \n\nEnvironment Variables\n---------------------\n\nIf you want or need to pass credentials via environment variables, you\ncan set the appropriate variables for your product of the following:\n\n::\n\n export SEARCHTWEETS_ENDPOINT=\n export SEARCHTWEETS_USERNAME=\n export SEARCHTWEETS_PASSWORD=\n export SEARCHTWEETS_BEARER_TOKEN=\n export SEARCHTWEETS_ACCOUNT_TYPE=\n export SEARCHTWEETS_CONSUMER_KEY=\n export SEARCHTWEETS_CONSUMER_SECRET=\n\nThe ``load_credentials`` function will attempt to find these variables\nif it cannot load fields from the YAML file, and it will **overwrite any\ncredentials from the YAML file that are present as environment\nvariables** if they have been parsed. This behavior can be changed by\nsetting the ``load_credentials`` parameter ``env_overwrite`` to\n``False``.\n\nThe following cells demonstrates credential handling in the Python\nlibrary.\n\n.. code:: python\n\n from searchtweets import load_credentials\n\n.. code:: python\n\n load_credentials(filename=\"./search_tweets_creds_example.yaml\",\n yaml_key=\"search_tweets_ent_example\",\n env_overwrite=False)\n\n::\n\n {'username': '',\n 'password': '',\n 'endpoint': ''}\n\n.. code:: python\n\n load_credentials(filename=\"./search_tweets_creds_example.yaml\",\n yaml_key=\"search_tweets_premium_example\",\n env_overwrite=False)\n\n::\n\n {'bearer_token': '',\n 'endpoint': 'https://api.twitter.com/1.1/tweets/search/30day/dev.json',\n 'extra_headers_dict': None}\n\nEnvironment Variable Overrides\n------------------------------\n\nIf we set our environment variables, the program will look for them\nregardless of a YAML file's validity or existence.\n\n.. code:: python\n\n import os\n os.environ[\"SEARCHTWEETS_USERNAME\"] = \"\"\n os.environ[\"SEARCHTWEETS_PASSWORD\"] = \"\"\n os.environ[\"SEARCHTWEETS_ENDPOINT\"] = \"\"\n\n load_credentials(filename=\"nothing_here.yaml\", yaml_key=\"no_key_here\")\n\n::\n\n cannot read file nothing_here.yaml\n Error parsing YAML file; searching for valid environment variables\n\n::\n\n {'username': '',\n 'password': '',\n 'endpoint': ''}\n\nCommand-line app\n----------------\n\nthe flags:\n\n- ``--credential-file ``\n- ``--credential-file-key ``\n- ``--env-overwrite``\n\nare used to control credential behavior from the command-line app.\n\n--------------\n\nUsing the Comand Line Application\n=================================\n\nThe library includes an application, ``search_tweets.py``, that provides\nrapid access to Tweets. When you use ``pip`` to install this package,\n``search_tweets.py`` is installed globally. The file is located in the\n``tools/`` directory for those who want to run it locally.\n\nNote that the ``--results-per-call`` flag specifies an argument to the\nAPI ( ``maxResults``, results returned per CALL), not as a hard max to\nnumber of results returned from this program. The argument\n``--max-results`` defines the maximum number of results to return from a\ngiven call. All examples assume that your credentials are set up\ncorrectly in the default location - ``.twitter_keys.yaml`` or in\nenvironment variables.\n\n**Stream json results to stdout without saving**\n\n.. code:: bash\n\n search_tweets.py \\\n --max-results 1000 \\\n --results-per-call 100 \\\n --filter-rule \"beyonce has:hashtags\" \\\n --print-stream\n\n**Stream json results to stdout and save to a file**\n\n.. code:: bash\n\n search_tweets.py \\\n --max-results 1000 \\\n --results-per-call 100 \\\n --filter-rule \"beyonce has:hashtags\" \\\n --filename-prefix beyonce_geo \\\n --print-stream\n\n**Save to file without output**\n\n.. code:: bash\n\n search_tweets.py \\\n --max-results 100 \\\n --results-per-call 100 \\\n --filter-rule \"beyonce has:hashtags\" \\\n --filename-prefix beyonce_geo \\\n --no-print-stream\n\nOne or more custom headers can be specified from the command line, using\nthe ``--extra-headers`` argument and a JSON-formatted string\nrepresenting a dictionary of extra headers:\n\n.. code:: bash\n\n search_tweets.py \\\n --filter-rule \"beyonce has:hashtags\" \\\n --extra-headers '{\"\":\"\"}'\n\nOptions can be passed via a configuration file (either ini or YAML).\nExample files can be found in the ``tools/api_config_example.config`` or\n``./tools/api_yaml_example.yaml`` files, which might look like this:\n\n.. code:: bash\n\n [search_rules]\n from_date = 2017-06-01\n to_date = 2017-09-01\n pt_rule = beyonce has:geo\n\n [search_params]\n results_per_call = 500\n max_results = 500\n\n [output_params]\n save_file = True\n filename_prefix = beyonce\n results_per_file = 10000000\n\nOr this:\n\n.. code:: yaml\n\n search_rules:\n from-date: 2017-06-01\n to-date: 2017-09-01 01:01\n pt-rule: kanye\n\n search_params:\n results-per-call: 500\n max-results: 500\n\n output_params:\n save_file: True\n filename_prefix: kanye\n results_per_file: 10000000\n\nCustom headers can be specified in a config file, under a specific\ncredentials key:\n\n.. code:: yaml\n\n search_tweets_api:\n account_type: premium\n endpoint: \n username: \n password: \n extra_headers:\n : \n\nWhen using a config file in conjunction with the command-line utility,\nyou need to specify your config file via the ``--config-file``\nparameter. Additional command-line arguments will either be *added* to\nthe config file args or **overwrite** the config file args if both are\nspecified and present.\n\nExample:\n\n::\n\n search_tweets.py \\\n --config-file myapiconfig.config \\\n --no-print-stream\n\n--------------\n\nFull options are listed below:\n\n::\n\n $ search_tweets.py -h\n usage: search_tweets.py [-h] [--credential-file CREDENTIAL_FILE]\n [--credential-file-key CREDENTIAL_YAML_KEY]\n [--env-overwrite ENV_OVERWRITE]\n [--config-file CONFIG_FILENAME]\n [--account-type {premium,enterprise}]\n [--count-bucket COUNT_BUCKET]\n [--start-datetime FROM_DATE] [--end-datetime TO_DATE]\n [--filter-rule PT_RULE]\n [--results-per-call RESULTS_PER_CALL]\n [--max-results MAX_RESULTS] [--max-pages MAX_PAGES]\n [--results-per-file RESULTS_PER_FILE]\n [--filename-prefix FILENAME_PREFIX]\n [--no-print-stream] [--print-stream]\n [--extra-headers EXTRA_HEADERS] [--debug]\n\n optional arguments:\n -h, --help show this help message and exit\n --credential-file CREDENTIAL_FILE\n Location of the yaml file used to hold your\n credentials.\n --credential-file-key CREDENTIAL_YAML_KEY\n the key in the credential file used for this session's\n credentials. Defaults to search_tweets_api\n --env-overwrite ENV_OVERWRITE\n Overwrite YAML-parsed credentials with any set\n environment variables. See API docs or readme for\n details.\n --config-file CONFIG_FILENAME\n configuration file with all parameters. Far, easier to\n use than the command-line args version., If a valid\n file is found, all args will be populated, from there.\n Remaining command-line args, will overrule args found\n in the config, file.\n --account-type {premium,enterprise}\n The account type you are using\n --count-bucket COUNT_BUCKET\n Bucket size for counts API. Options:, day, hour,\n minute (default is 'day').\n --start-datetime FROM_DATE\n Start of datetime window, format 'YYYY-mm-DDTHH:MM'\n (default: -30 days)\n --end-datetime TO_DATE\n End of datetime window, format 'YYYY-mm-DDTHH:MM'\n (default: most recent date)\n --filter-rule PT_RULE\n PowerTrack filter rule (See: http://support.gnip.com/c\n ustomer/portal/articles/901152-powertrack-operators)\n --results-per-call RESULTS_PER_CALL\n Number of results to return per call (default 100; max\n 500) - corresponds to 'maxResults' in the API\n --max-results MAX_RESULTS\n Maximum number of Tweets or Counts to return for this\n session (defaults to 500)\n --max-pages MAX_PAGES\n Maximum number of pages/API calls to use for this\n session.\n --results-per-file RESULTS_PER_FILE\n Maximum tweets to save per file.\n --filename-prefix FILENAME_PREFIX\n prefix for the filename where tweet json data will be\n stored.\n --no-print-stream disable print streaming\n --print-stream Print tweet stream to stdout \n --extra-headers EXTRA_HEADERS\n JSON-formatted str representing a dict of additional\n request headers\n --debug print all info and warning messages\n\n--------------\n\nUsing the Twitter Search APIs' Python Wrapper\n=============================================\n\nWorking with the API within a Python program is straightforward both for\nPremium and Enterprise clients.\n\nWe'll assume that credentials are in the default location,\n``~/.twitter_keys.yaml``.\n\n.. code:: python\n\n from searchtweets import ResultStream, gen_rule_payload, load_credentials\n\nEnterprise setup\n----------------\n\n.. code:: python\n\n enterprise_search_args = load_credentials(\"~/.twitter_keys.yaml\",\n yaml_key=\"search_tweets_enterprise\",\n env_overwrite=False)\n\nPremium Setup\n-------------\n\n.. code:: python\n\n premium_search_args = load_credentials(\"~/.twitter_keys.yaml\",\n yaml_key=\"search_tweets_premium\",\n env_overwrite=False)\n\nThere is a function that formats search API rules into valid json\nqueries called ``gen_rule_payload``. It has sensible defaults, such as\npulling more Tweets per call than the default 100 (but note that a\nsandbox environment can only have a max of 100 here, so if you get\nerrors, please check this) not including dates, and defaulting to hourly\ncounts when using the counts api. Discussing the finer points of\ngenerating search rules is out of scope for these examples; I encourage\nyou to see the docs to learn the nuances within, but for now let's see\nwhat a rule looks like.\n\n.. code:: python\n\n rule = gen_rule_payload(\"beyonce\", results_per_call=100) # testing with a sandbox account\n print(rule)\n\n::\n\n {\"query\":\"beyonce\",\"maxResults\":100}\n\nThis rule will match tweets that have the text ``beyonce`` in them.\n\nFrom this point, there are two ways to interact with the API. There is a\nquick method to collect smaller amounts of Tweets to memory that\nrequires less thought and knowledge, and interaction with the\n``ResultStream`` object which will be introduced later.\n\nFast Way\n--------\n\nWe'll use the ``search_args`` variable to power the configuration point\nfor the API. The object also takes a valid PowerTrack rule and has\noptions to cutoff search when hitting limits on both number of Tweets\nand API calls.\n\nWe'll be using the ``collect_results`` function, which has three\nparameters.\n\n- rule: a valid PowerTrack rule, referenced earlier\n- max_results: as the API handles pagination, it will stop collecting\n when we get to this number\n- result_stream_args: configuration args that we've already specified.\n\nFor the remaining examples, please change the args to either premium or\nenterprise depending on your usage.\n\nLet's see how it goes:\n\n.. code:: python\n\n from searchtweets import collect_results\n\n.. code:: python\n\n tweets = collect_results(rule,\n max_results=100,\n result_stream_args=enterprise_search_args) # change this if you need to\n\nBy default, Tweet payloads are lazily parsed into a ``Tweet``\n`object `__. An overwhelming\nnumber of Tweet attributes are made available directly, as such:\n\n.. code:: python\n\n [print(tweet.all_text, end='\\n\\n') for tweet in tweets[0:10]];\n\n::\n\n Jay-Z & Beyonc\u00e9 sat across from us at dinner tonight and, at one point, I made eye contact with Beyonc\u00e9. My limbs turned to jello and I can no longer form a coherent sentence. I have seen the eyes of the lord.\n\n Beyonc\u00e9 and it isn't close. https://t.co/UdOU9oUtuW\n\n As you could guess.. Signs by Beyonc\u00e9 will always be my shit.\n\n When Beyonc\u00e9 adopts a dog \ud83d\ude4c\ud83c\udffe https://t.co/U571HyLG4F\n\n Hold up, you can't just do that to Beyonc\u00e9\n https://t.co/3p14DocGqA\n\n Why y'all keep using Rihanna and Beyonc\u00e9 gifs to promote the show when y'all let Bey lose the same award she deserved 3 times and let Rihanna leave with nothing but the clothes on her back? https://t.co/w38QpH0wma\n\n 30) anybody tell you that you look like Beyonc\u00e9 https://t.co/Vo4Z7bfSCi\n\n Mi Beyonc\u00e9 favorita https://t.co/f9Jp600l2B\n Beyonc\u00e9 necesita ver esto. Que diosa @TiniStoessel \ud83d\udd25\ud83d\udd25\ud83d\udd25 https://t.co/gadVJbehQZ\n\n Joanne Pearce Is now playing IF I WAS A BOY - BEYONCE.mp3 by !\n\n I'm trynna see beyonc\u00e9's finsta before I die\n\n.. code:: python\n\n [print(tweet.created_at_datetime) for tweet in tweets[0:10]];\n\n::\n\n 2018-01-17 00:08:50\n 2018-01-17 00:08:49\n 2018-01-17 00:08:44\n 2018-01-17 00:08:42\n 2018-01-17 00:08:42\n 2018-01-17 00:08:42\n 2018-01-17 00:08:40\n 2018-01-17 00:08:38\n 2018-01-17 00:08:37\n 2018-01-17 00:08:37\n\n.. code:: python\n\n [print(tweet.generator.get(\"name\")) for tweet in tweets[0:10]];\n\n::\n\n Twitter for iPhone\n Twitter for iPhone\n Twitter for iPhone\n Twitter for iPhone\n Twitter for iPhone\n Twitter for iPhone\n Twitter for Android\n Twitter for iPhone\n Airtime Pro\n Twitter for iPhone\n\nVoila, we have some Tweets. For interactive environments and other cases\nwhere you don't care about collecting your data in a single load or\ndon't need to operate on the stream of Tweets or counts directly, I\nrecommend using this convenience function.\n\nWorking with the ResultStream\n-----------------------------\n\nThe ResultStream object will be powered by the ``search_args``, and\ntakes the rules and other configuration parameters, including a hard\nstop on number of pages to limit your API call usage.\n\n.. code:: python\n\n rs = ResultStream(rule_payload=rule,\n max_results=500,\n max_pages=1,\n **premium_search_args)\n\n print(rs)\n\n::\n\n ResultStream: \n \t{\n \"username\":null,\n \"endpoint\":\"https:\\/\\/api.twitter.com\\/1.1\\/tweets\\/search\\/30day\\/dev.json\",\n \"rule_payload\":{\n \"query\":\"beyonce\",\n \"maxResults\":100\n },\n \"tweetify\":true,\n \"max_results\":500\n }\n\nThere is a function, ``.stream``, that seamlessly handles requests and\npagination for a given query. It returns a generator, and to grab our\n500 Tweets that mention ``beyonce`` we can do this:\n\n.. code:: python\n\n tweets = list(rs.stream())\n\nTweets are lazily parsed using our `Tweet\nParser `__, so tweet data is\nvery easily extractable.\n\n.. code:: python\n\n # using unidecode to prevent emoji/accents printing \n [print(tweet.all_text) for tweet in tweets[0:10]];\n\n::\n\n gente socorro kkkkkkkkkk BEYONCE https://t.co/kJ9zubvKuf\n Jay-Z & Beyonc\u00e9 sat across from us at dinner tonight and, at one point, I made eye contact with Beyonc\u00e9. My limbs turned to jello and I can no longer form a coherent sentence. I have seen the eyes of the lord.\n Beyonc\u00e9 and it isn't close. https://t.co/UdOU9oUtuW\n As you could guess.. Signs by Beyonc\u00e9 will always be my shit.\n When Beyonc\u00e9 adopts a dog \ud83d\ude4c\ud83c\udffe https://t.co/U571HyLG4F\n Hold up, you can't just do that to Beyonc\u00e9\n https://t.co/3p14DocGqA\n Why y'all keep using Rihanna and Beyonc\u00e9 gifs to promote the show when y'all let Bey lose the same award she deserved 3 times and let Rihanna leave with nothing but the clothes on her back? https://t.co/w38QpH0wma\n 30) anybody tell you that you look like Beyonc\u00e9 https://t.co/Vo4Z7bfSCi\n Mi Beyonc\u00e9 favorita https://t.co/f9Jp600l2B\n Beyonc\u00e9 necesita ver esto. Que diosa @TiniStoessel \ud83d\udd25\ud83d\udd25\ud83d\udd25 https://t.co/gadVJbehQZ\n Joanne Pearce Is now playing IF I WAS A BOY - BEYONCE.mp3 by !\n\nCounts Endpoint\n---------------\n\nWe can also use the Search API Counts endpoint to get counts of Tweets\nthat match our rule. Each request will return up to *30* results, and\neach count request can be done on a minutely, hourly, or daily basis.\nThe underlying ``ResultStream`` object will handle converting your\nendpoint to the count endpoint, and you have to specify the\n``count_bucket`` argument when making a rule to use it.\n\nThe process is very similar to grabbing Tweets, but has some minor\ndifferences.\n\n*Caveat - premium sandbox environments do NOT have access to the Search\nAPI counts endpoint.*\n\n.. code:: python\n\n count_rule = gen_rule_payload(\"beyonce\", count_bucket=\"day\")\n\n counts = collect_results(count_rule, result_stream_args=enterprise_search_args)\n\nOur results are pretty straightforward and can be rapidly used.\n\n.. code:: python\n\n counts\n\n::\n\n [{'count': 366, 'timePeriod': '201801170000'},\n {'count': 44580, 'timePeriod': '201801160000'},\n {'count': 61932, 'timePeriod': '201801150000'},\n {'count': 59678, 'timePeriod': '201801140000'},\n {'count': 44014, 'timePeriod': '201801130000'},\n {'count': 46607, 'timePeriod': '201801120000'},\n {'count': 41523, 'timePeriod': '201801110000'},\n {'count': 47056, 'timePeriod': '201801100000'},\n {'count': 65506, 'timePeriod': '201801090000'},\n {'count': 95251, 'timePeriod': '201801080000'},\n {'count': 162883, 'timePeriod': '201801070000'},\n {'count': 106344, 'timePeriod': '201801060000'},\n {'count': 93542, 'timePeriod': '201801050000'},\n {'count': 110415, 'timePeriod': '201801040000'},\n {'count': 127523, 'timePeriod': '201801030000'},\n {'count': 131952, 'timePeriod': '201801020000'},\n {'count': 176157, 'timePeriod': '201801010000'},\n {'count': 57229, 'timePeriod': '201712310000'},\n {'count': 72277, 'timePeriod': '201712300000'},\n {'count': 72051, 'timePeriod': '201712290000'},\n {'count': 76371, 'timePeriod': '201712280000'},\n {'count': 61578, 'timePeriod': '201712270000'},\n {'count': 55118, 'timePeriod': '201712260000'},\n {'count': 59115, 'timePeriod': '201712250000'},\n {'count': 106219, 'timePeriod': '201712240000'},\n {'count': 114732, 'timePeriod': '201712230000'},\n {'count': 73327, 'timePeriod': '201712220000'},\n {'count': 89171, 'timePeriod': '201712210000'},\n {'count': 192381, 'timePeriod': '201712200000'},\n {'count': 85554, 'timePeriod': '201712190000'},\n {'count': 57829, 'timePeriod': '201712180000'}]\n\nDated searches / Full Archive Search\n------------------------------------\n\n**Note that this will only work with the full archive search option**,\nwhich is available to my account only via the enterprise options. Full\narchive search will likely require a different endpoint or access\nmethod; please see your developer console for details.\n\nLet's make a new rule and pass it dates this time.\n\n``gen_rule_payload`` takes timestamps of the following forms:\n\n- ``YYYYmmDDHHMM``\n- ``YYYY-mm-DD`` (which will convert to midnight UTC (00:00)\n- ``YYYY-mm-DD HH:MM``\n- ``YYYY-mm-DDTHH:MM``\n\nNote - all Tweets are stored in UTC time.\n\n.. code:: python\n\n rule = gen_rule_payload(\"from:jack\",\n from_date=\"2017-09-01\", #UTC 2017-09-01 00:00\n to_date=\"2017-10-30\",#UTC 2017-10-30 00:00\n results_per_call=500)\n print(rule)\n\n::\n\n {\"query\":\"from:jack\",\"maxResults\":500,\"toDate\":\"201710300000\",\"fromDate\":\"201709010000\"}\n\n.. code:: python\n\n tweets = collect_results(rule, max_results=500, result_stream_args=enterprise_search_args)\n\n.. code:: python\n\n [print(tweet.all_text) for tweet in tweets[0:10]];\n\n::\n\n More clarity on our private information policy and enforcement. Working to build as much direct context into the product too https://t.co/IrwBexPrBA\n To provide more clarity on our private information policy, we\u2019ve added specific examples of what is/is not a violation and insight into what we need to remove this type of content from the service. https://t.co/NGx5hh2tTQ\n Launching violent groups and hateful images/symbols policy on November 22nd https://t.co/NaWuBPxyO5\n We will now launch our policies on violent groups and hateful imagery and hate symbols on Nov 22. During the development process, we received valuable feedback that we\u2019re implementing before these are published and enforced. See more on our policy development process here \ud83d\udc47 https://t.co/wx3EeH39BI\n @WillStick @lizkelley Happy birthday Liz!\n Off-boarding advertising from all accounts owned by Russia Today (RT) and Sputnik.\n\n We\u2019re donating all projected earnings ($1.9mm) to support external research into the use of Twitter in elections, including use of malicious automation and misinformation. https://t.co/zIxfqqXCZr\n @TMFJMo @anthonynoto Thank you\n @gasca @stratechery @Lefsetz letter\n @gasca @stratechery Bridgewater\u2019s Daily Observations\n Yup!!!! \u2764\ufe0f\u2764\ufe0f\u2764\ufe0f\u2764\ufe0f #davechappelle https://t.co/ybSGNrQpYF\n @ndimichino Sometimes\n Setting up at @CampFlogGnaw https://t.co/nVq8QjkKsf\n\n.. code:: python\n\n rule = gen_rule_payload(\"from:jack\",\n from_date=\"2017-09-20\",\n to_date=\"2017-10-30\",\n count_bucket=\"day\",\n results_per_call=500)\n print(rule)\n\n::\n\n {\"query\":\"from:jack\",\"toDate\":\"201710300000\",\"fromDate\":\"201709200000\",\"bucket\":\"day\"}\n\n.. code:: python\n\n counts = collect_results(rule, max_results=500, result_stream_args=enterprise_search_args)\n\n.. code:: python\n\n [print(c) for c in counts];\n\n::\n\n {'timePeriod': '201710290000', 'count': 0}\n {'timePeriod': '201710280000', 'count': 0}\n {'timePeriod': '201710270000', 'count': 3}\n {'timePeriod': '201710260000', 'count': 6}\n {'timePeriod': '201710250000', 'count': 4}\n {'timePeriod': '201710240000', 'count': 4}\n {'timePeriod': '201710230000', 'count': 0}\n {'timePeriod': '201710220000', 'count': 0}\n {'timePeriod': '201710210000', 'count': 3}\n {'timePeriod': '201710200000', 'count': 2}\n {'timePeriod': '201710190000', 'count': 1}\n {'timePeriod': '201710180000', 'count': 6}\n {'timePeriod': '201710170000', 'count': 2}\n {'timePeriod': '201710160000', 'count': 2}\n {'timePeriod': '201710150000', 'count': 1}\n {'timePeriod': '201710140000', 'count': 64}\n {'timePeriod': '201710130000', 'count': 3}\n {'timePeriod': '201710120000', 'count': 4}\n {'timePeriod': '201710110000', 'count': 8}\n {'timePeriod': '201710100000', 'count': 4}\n {'timePeriod': '201710090000', 'count': 1}\n {'timePeriod': '201710080000', 'count': 0}\n {'timePeriod': '201710070000', 'count': 0}\n {'timePeriod': '201710060000', 'count': 1}\n {'timePeriod': '201710050000', 'count': 3}\n {'timePeriod': '201710040000', 'count': 5}\n {'timePeriod': '201710030000', 'count': 8}\n {'timePeriod': '201710020000', 'count': 5}\n {'timePeriod': '201710010000', 'count': 0}\n {'timePeriod': '201709300000', 'count': 0}\n {'timePeriod': '201709290000', 'count': 0}\n {'timePeriod': '201709280000', 'count': 9}\n {'timePeriod': '201709270000', 'count': 41}\n {'timePeriod': '201709260000', 'count': 13}\n {'timePeriod': '201709250000', 'count': 6}\n {'timePeriod': '201709240000', 'count': 7}\n {'timePeriod': '201709230000', 'count': 3}\n {'timePeriod': '201709220000', 'count': 0}\n {'timePeriod': '201709210000', 'count': 1}\n {'timePeriod': '201709200000', 'count': 7}\n\nContributing\n============\n\nAny contributions should follow the following pattern:\n\n1. Make a feature or bugfix branch, e.g.,\n ``git checkout -b my_new_feature``\n2. Make your changes in that branch\n3. Ensure you bump the version number in ``searchtweets/_version.py`` to\n reflect your changes. We use `Semantic\n Versioning `__, so non-breaking enhancements\n should increment the minor version, e.g., ``1.5.0 -> 1.6.0``, and\n bugfixes will increment the last version, ``1.6.0 -> 1.6.1``.\n4. Create a pull request\n\nAfter the pull request process is accepted, package maintainers will\nhandle building documentation and distribution to Pypi.\n\nFor reference, distributing to Pypi is accomplished by the following\ncommands, ran from the root directory in the repo:\n\n.. code:: bash\n\n python setup.py bdist_wheel\n python setup.py sdist\n twine upload dist/*\n\nHow to build the documentation:\n\nBuilding the documentation requires a few Sphinx packages to build the\nwebpages:\n\n.. code:: bash\n\n pip install sphinx\n pip install sphinx_bootstrap_theme\n pip install sphinxcontrib-napoleon\n\nThen (once your changes are committed to master) you should be able to\nrun the documentation-generating bash script and follow the\ninstructions:\n\n.. code:: bash\n\n bash build_sphinx_docs.sh master searchtweets\n\nNote that this README is also generated, and so after any README changes\nyou'll need to re-build the README (you need pandoc version 2.1+ for\nthis) and commit the result:\n\n.. code:: bash\n\n bash make_readme.sh\n\n\n", "description_content_type": "", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/twitterdev/search-tweets-python", "keywords": "", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "searchtweets", "package_url": "https://pypi.org/project/searchtweets/", "platform": "", "project_url": "https://pypi.org/project/searchtweets/", "project_urls": { "Homepage": "https://github.com/twitterdev/search-tweets-python" }, "release_url": "https://pypi.org/project/searchtweets/1.7.4/", "requires_dist": [ "requests", "tweet-parser", "pyyaml" ], "requires_python": ">=3.3", "summary": "Wrapper for Twitter's Premium and Enterprise search APIs", "version": "1.7.4" }, "last_serial": 4384799, "releases": { "1.0": [ { "comment_text": "", "digests": { "md5": "606e8552934393a3d7e990b3f78371e5", "sha256": "9fe9c5c44846d216002db5912038d37ed314c17817c6be3d05f6e459afe9bdfe" }, "downloads": -1, "filename": "searchtweets-1.0.tar.gz", "has_sig": false, "md5_digest": "606e8552934393a3d7e990b3f78371e5", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 25464, "upload_time": "2018-01-08T21:36:36", "url": "https://files.pythonhosted.org/packages/1a/54/06c559fbcbb35d01da2e877a0a61174f7003c18e8b138a88cf1e97cc026b/searchtweets-1.0.tar.gz" } ], "1.2.1": [ { "comment_text": "", "digests": { "md5": "1b0954970746398ee22633eb872d6dcb", "sha256": "46297ad8f59dbd912ab7ae7cf93b73c23dfd0e807d0c3688ac55aea54d6ac9d5" }, "downloads": -1, "filename": "searchtweets-1.2.1-py3-none-any.whl", "has_sig": false, "md5_digest": "1b0954970746398ee22633eb872d6dcb", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 32018, "upload_time": "2018-01-19T22:48:16", "url": "https://files.pythonhosted.org/packages/8c/ce/47a2b68363a0b03577d76cea84df8ba0392d39cbaa5f6e3a92d1e0d8df03/searchtweets-1.2.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "5261c71f328949f472b84daa5631b98a", "sha256": "f64360efb9cf14053286b21a18f029a26f5145ceeaf6870fc7941c0456a7003a" }, "downloads": -1, "filename": "searchtweets-1.2.1.tar.gz", "has_sig": false, "md5_digest": "5261c71f328949f472b84daa5631b98a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 29474, "upload_time": "2018-01-19T22:48:18", "url": "https://files.pythonhosted.org/packages/34/7f/0442d34a8929f2b37b598d70906c6df8fcabb426e1061d93a75ee6cc39ce/searchtweets-1.2.1.tar.gz" } ], "1.3.0": [ { "comment_text": "", "digests": { "md5": "f22b7eabbfec7506263952c2ce72acab", "sha256": "688948a0971c74016b84f6677080f428ab70c9d28546b047de867efb4073206d" }, "downloads": -1, "filename": "searchtweets-1.3.0.tar.gz", "has_sig": false, "md5_digest": "f22b7eabbfec7506263952c2ce72acab", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.3", "size": 33001, "upload_time": "2018-02-20T19:41:48", "url": "https://files.pythonhosted.org/packages/ca/60/1d6b9f8a4989ee12c5116603b257cc3af622b5f7f88b54d3251497478a87/searchtweets-1.3.0.tar.gz" } ], "1.3.1": [ { "comment_text": "", "digests": { "md5": "394bbe153d7f1ae86db4b1e657641a18", "sha256": "9d98b170af5f72581c14c8e80a9599335292deb418e78d7e2e1cb43b045bf206" }, "downloads": -1, "filename": "searchtweets-1.3.1-py3-none-any.whl", "has_sig": false, "md5_digest": "394bbe153d7f1ae86db4b1e657641a18", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.3", "size": 33474, "upload_time": "2018-03-07T16:52:50", "url": "https://files.pythonhosted.org/packages/db/e8/fb7cf2bed3314fdc5f387b662a4fe82d1df28edf77c4aad9186655412dd5/searchtweets-1.3.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "cd891a60d4e082a4ea09251764e5f748", "sha256": "4829deb7d340c409c888f81b1ae990a219b0e344081d9c7cf329513f914adae7" }, "downloads": -1, "filename": "searchtweets-1.3.1.tar.gz", "has_sig": false, "md5_digest": "cd891a60d4e082a4ea09251764e5f748", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.3", "size": 32988, "upload_time": "2018-03-07T16:52:52", "url": "https://files.pythonhosted.org/packages/8a/9c/51fbf85394d4fa9640ac8e4e110304aef50559f6843164a318fdbd27829e/searchtweets-1.3.1.tar.gz" } ], "1.3.2": [ { "comment_text": "", "digests": { "md5": "0873a630e72a6c64be50efdee7ceff40", "sha256": "28e0da3cd22f6da60a4dc93dc23df0add6638293b5f83b71cfe51c3e8fbbdd1f" }, "downloads": -1, "filename": "searchtweets-1.3.2-py3-none-any.whl", "has_sig": false, "md5_digest": "0873a630e72a6c64be50efdee7ceff40", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.3", "size": 33612, "upload_time": "2018-04-12T19:28:32", "url": "https://files.pythonhosted.org/packages/8c/75/ce9e149fd22ca9cf017a5a8780b12815668415a3e0c8b78bf4eb0ed73cc6/searchtweets-1.3.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "fdeaf061603f6b31b3b5a4270d37b1ef", "sha256": "91899339db2a451a46b63801e453cd672109281be89f8f97c821bca1b7911c9b" }, "downloads": -1, "filename": "searchtweets-1.3.2.tar.gz", "has_sig": false, "md5_digest": "fdeaf061603f6b31b3b5a4270d37b1ef", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.3", "size": 33608, "upload_time": "2018-04-12T19:28:34", "url": "https://files.pythonhosted.org/packages/98/5b/40af2dbd6a446faaa044286b0566fa52d040899c1368934f181973ff3414/searchtweets-1.3.2.tar.gz" } ], "1.4.0": [ { "comment_text": "", "digests": { "md5": "66edcfd2de107b5f313148a4a9ab42da", "sha256": "0be887b08347626a6fbdc9f53df89999a44f76eb5359b6c93b09853f0355eed3" }, "downloads": -1, "filename": "searchtweets-1.4.0-py3-none-any.whl", "has_sig": false, "md5_digest": "66edcfd2de107b5f313148a4a9ab42da", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.3", "size": 32992, "upload_time": "2018-04-13T20:07:44", "url": "https://files.pythonhosted.org/packages/b1/a6/221bf9d1ec7a2145dfe667569db45c528ec8bd7ad3d13beed6e2310b1362/searchtweets-1.4.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "41780c68538c4d0b40fdd860241bddf0", "sha256": "494b4c8e51ad873e4a73a8bd3cfda819745ea4720f87fb7ed471fd5136ded04d" }, "downloads": -1, "filename": "searchtweets-1.4.0.tar.gz", "has_sig": false, "md5_digest": "41780c68538c4d0b40fdd860241bddf0", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.3", "size": 33084, "upload_time": "2018-04-13T20:07:45", "url": "https://files.pythonhosted.org/packages/93/e3/612fff306c7ea887ccff2c4634b5871c75d836bd2ff5ff0f9748e15e092d/searchtweets-1.4.0.tar.gz" } ], "1.5.0": [ { "comment_text": "", "digests": { "md5": "a099c682b2785d9ada6e261ee37dbf33", "sha256": "5af0cf95290d1cda73217028bea09265d739046963cbba0076043b9eded3a9ce" }, "downloads": -1, "filename": "searchtweets-1.5.0-py3-none-any.whl", "has_sig": false, "md5_digest": "a099c682b2785d9ada6e261ee37dbf33", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.3", "size": 33032, "upload_time": "2018-04-13T21:02:43", "url": "https://files.pythonhosted.org/packages/40/f5/a9aae76f95511f89be62fad4c43337339784ef0414244160be2002f11f16/searchtweets-1.5.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "568920e6d71d91f5c255ec5a3fbbef2d", "sha256": "a6568e3acdce6ebc49f7c4877a9ab41932a54bdc44e9e522e99e83f2c3d51c1b" }, "downloads": -1, "filename": "searchtweets-1.5.0.tar.gz", "has_sig": false, "md5_digest": "568920e6d71d91f5c255ec5a3fbbef2d", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.3", "size": 33109, "upload_time": "2018-04-13T21:02:45", "url": "https://files.pythonhosted.org/packages/44/0e/224cd71911aeac3c6aa0318a7af6104c0a8da3ffd1baa3c731c982612f29/searchtweets-1.5.0.tar.gz" } ], "1.7.0": [ { "comment_text": "", "digests": { "md5": "5a46e5be52c8e895b867fc926d6f93f2", "sha256": "5b5cad162f6c59cf722ab252896d782d2e227b621aee6e6f21b3281e812dc5f1" }, "downloads": -1, "filename": "searchtweets-1.7.0-py3-none-any.whl", "has_sig": false, "md5_digest": "5a46e5be52c8e895b867fc926d6f93f2", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.3", "size": 24641, "upload_time": "2018-04-18T21:12:11", "url": "https://files.pythonhosted.org/packages/9e/19/69c724fcf21716f8802fc7b143431715da4a9485a6ceca579ea0cace6e0b/searchtweets-1.7.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "92ab70d314b8f54a0006d95ced675c8f", "sha256": "f2718ad6bf6e39af97e1cb4265cbf5aeedd8d78cbafb732751c05de56dabf50b" }, "downloads": -1, "filename": "searchtweets-1.7.0.tar.gz", "has_sig": false, "md5_digest": "92ab70d314b8f54a0006d95ced675c8f", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.3", "size": 36867, "upload_time": "2018-04-18T21:12:12", "url": "https://files.pythonhosted.org/packages/9c/e4/1b6278100ceee532cce31b33c538656909ac968bf63619dd126aeebe8dbe/searchtweets-1.7.0.tar.gz" } ], "1.7.1": [ { "comment_text": "", "digests": { "md5": "1857b2099c0826f384a247a0fb564242", "sha256": "385942abe01cb7e02b41db520f54f33b52459a09c110c52f5427ee62341000ec" }, "downloads": -1, "filename": "searchtweets-1.7.1-py3-none-any.whl", "has_sig": false, "md5_digest": "1857b2099c0826f384a247a0fb564242", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.3", "size": 24859, "upload_time": "2018-06-12T21:51:50", "url": "https://files.pythonhosted.org/packages/10/be/f39b410011a55c9f0f8dbddf4e0f92fe22b53baf41cd83668b5cdb6b8596/searchtweets-1.7.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "3a490a802995a0e246eb50d6a13149e3", "sha256": "cc01992293be10149806b8e1457e3b5bfe2974ea66b83b2e34b972119a098f4f" }, "downloads": -1, "filename": "searchtweets-1.7.1.tar.gz", "has_sig": false, "md5_digest": "3a490a802995a0e246eb50d6a13149e3", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.3", "size": 37093, "upload_time": "2018-06-12T21:51:52", "url": "https://files.pythonhosted.org/packages/52/a1/b7b1344927f46f5cdaf4eb4f67437bd2daa729cd3a6e2b0bb572afdf09f9/searchtweets-1.7.1.tar.gz" } ], "1.7.2": [ { "comment_text": "", "digests": { "md5": "6af6359defffc1bdc14bba0d28ffd3df", "sha256": "52ff148abc50c8c96d04434a048067594639e8194fcfa694f0d66ebe7846893d" }, "downloads": -1, "filename": "searchtweets-1.7.2-py3-none-any.whl", "has_sig": false, "md5_digest": "6af6359defffc1bdc14bba0d28ffd3df", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.3", "size": 25690, "upload_time": "2018-10-01T17:45:00", "url": "https://files.pythonhosted.org/packages/08/a4/23784f1668bb987b90c37c55158e1bb84d840aaf3c637d512c802f38f29d/searchtweets-1.7.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "fb5ee288035251545e282a231922598c", "sha256": "b957de79a76ec3ffc0ba6a4f2b801a0bb476fe6c8dce593743d4fafcb52ab42c" }, "downloads": -1, "filename": "searchtweets-1.7.2.tar.gz", "has_sig": false, "md5_digest": "fb5ee288035251545e282a231922598c", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.3", "size": 36610, "upload_time": "2018-10-01T17:45:01", "url": "https://files.pythonhosted.org/packages/84/69/26eaa0c1e0bb4d9dc5dd0538ceb39b0e53a4ca6799f9d360e3e3f7d07093/searchtweets-1.7.2.tar.gz" } ], "1.7.4": [ { "comment_text": "", "digests": { "md5": "6b828086473f8ab0bca9a7c172429498", "sha256": "25cb699bfae54b091cacadc271b570a34bd834b6cab43780fa68adf0f5778036" }, "downloads": -1, "filename": "searchtweets-1.7.4-py3-none-any.whl", "has_sig": false, "md5_digest": "6b828086473f8ab0bca9a7c172429498", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.3", "size": 26139, "upload_time": "2018-10-17T03:22:36", "url": "https://files.pythonhosted.org/packages/51/d7/7dd296ba9469e046bad23583aaa0d36b18c7d6e4df9fd2acfb433d1c7ee2/searchtweets-1.7.4-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "21c8aa9de5b6d1f9c28e9972da6c4663", "sha256": "87fd54845aae2c826cc766dbb9e8fb31b2efd0b36f7492a965bf468f6fc0b9b2" }, "downloads": -1, "filename": "searchtweets-1.7.4.tar.gz", "has_sig": false, "md5_digest": "21c8aa9de5b6d1f9c28e9972da6c4663", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.3", "size": 38353, "upload_time": "2018-10-17T03:22:38", "url": "https://files.pythonhosted.org/packages/69/c3/8ce13857a65122a81b7138d0474b4b1a6c5d291e723ef0fa179b18827713/searchtweets-1.7.4.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "6b828086473f8ab0bca9a7c172429498", "sha256": "25cb699bfae54b091cacadc271b570a34bd834b6cab43780fa68adf0f5778036" }, "downloads": -1, "filename": "searchtweets-1.7.4-py3-none-any.whl", "has_sig": false, "md5_digest": "6b828086473f8ab0bca9a7c172429498", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.3", "size": 26139, "upload_time": "2018-10-17T03:22:36", "url": "https://files.pythonhosted.org/packages/51/d7/7dd296ba9469e046bad23583aaa0d36b18c7d6e4df9fd2acfb433d1c7ee2/searchtweets-1.7.4-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "21c8aa9de5b6d1f9c28e9972da6c4663", "sha256": "87fd54845aae2c826cc766dbb9e8fb31b2efd0b36f7492a965bf468f6fc0b9b2" }, "downloads": -1, "filename": "searchtweets-1.7.4.tar.gz", "has_sig": false, "md5_digest": "21c8aa9de5b6d1f9c28e9972da6c4663", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.3", "size": 38353, "upload_time": "2018-10-17T03:22:38", "url": "https://files.pythonhosted.org/packages/69/c3/8ce13857a65122a81b7138d0474b4b1a6c5d291e723ef0fa179b18827713/searchtweets-1.7.4.tar.gz" } ] }